diff --git a/.gitignore b/.gitignore index 0120a88..58f184c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,7 @@ build/ node_modules/ dato-builder.config.* coverage/ -src/datocms/ -src/.itemTypeBuilderCache.json \ No newline at end of file +#src/datocms/ +src/.itemTypeBuilderCache.json +.dato-builder-cache +CLAUDE.md \ No newline at end of file diff --git a/.npmignore b/.npmignore index bae63d2..24240f6 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,47 @@ -src -tests -tsconfig.json \ No newline at end of file +# Source files +src/ +tests/ +*.test.ts +*.test.js +__tests__/ + +# Configuration files +tsconfig.json +tsconfig.build.json +jest.config.js +.env +.env.* +*.config.js +*.config.mjs +*.config.ts + +# Development files +.vscode/ +.idea/ +*.log +*.tmp +*.temp +coverage/ +.nyc_output/ +.cache/ + +# Git and CI +.git/ +.github/ +.gitignore +.gitattributes +lefthook.yml +commitlint.config.js +release.config.mjs + +# Documentation +README.md +CHANGELOG.md +docs/ +*.md + +# Build artifacts +build/.tsbuildinfo +node_modules/ +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/README.md b/README.md index 6606ebf..6ce30c5 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,14 @@ module.exports = { debug: false, // enable verbose logging modelApiKeySuffix: undefined, // suffix for model API keys (e.g. "page" becomes "page_model") blockApiKeySuffix: "block", // suffix for block API keys (e.g. "hero" becomes "hero_block") + + // Paths for building from local to DatoCMS + modelsPath: "./datocms/models", // where to find model definitions + blocksPath: "./datocms/blocks", // where to find block definitions + + // Paths for syncing from DatoCMS to local + syncModelsPath: "./src/datocms/models", // where to generate synced models + syncBlocksPath: "./src/datocms/blocks", // where to generate synced blocks }; ``` @@ -97,11 +105,18 @@ module.exports = { ## CLI Commands -- **`npx dato-builder run `** - Build one or more scripts (blocks, models, or whole folders). +- **`npx dato-builder build`** + Build DatoCMS types and blocks from your local definitions. + +- **`npx dato-builder sync [options]`** + Sync blocks and models from DatoCMS to local TypeScript files. + + Options: + - `--dry-run`: Show what would be synced without making any changes + - `--force`: Force sync all items, ignoring cache - **`npx dato-builder clear-cache`** - Wipe the local cache that tracks what’s already been synced. + Wipe the local cache that tracks what's already been synced. --- @@ -169,12 +184,29 @@ npx dato-builder run datocms/models/TestModel.ts ### 3. Build Everything ```bash -npx dato-builder run datocms/ +npx dato-builder build ``` +### 4. Sync from DatoCMS + +You can also sync existing blocks and models from DatoCMS to your local project: + +```bash +# Dry run to see what would be synced +npx dato-builder sync --dry-run + +# Sync all changes +npx dato-builder sync + +# Force sync everything, ignoring cache +npx dato-builder sync --force +``` + +This will generate TypeScript files in your configured sync paths (default: `./src/datocms/blocks` and `./src/datocms/models`) that match your DatoCMS schema. + --- -_Note: You can add more builder scripts (blocks or models) and then point `run` at the parent folder to sync them all in one go._ +_Note: The sync command creates a single source of truth by generating local files from your DatoCMS schema, while the build command pushes your local definitions to DatoCMS._ # Comprehensive Field & Validator Reference diff --git a/bin/cli.js b/bin/cli.js index 805ebe0..b01e269 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,533 +1,5 @@ #!/usr/bin/env node -const { spawn } = require("node:child_process"); -const path = require("node:path"); -const fs = require("node:fs"); +const { setupCLI } = require("../build/cli"); -const SUPPORTED_EXTENSIONS = [".ts", ".js", ".mts", ".mjs"]; -const ESM_EXTENSIONS = [".mjs", ".mts"]; -const TS_EXTENSIONS = [".ts", ".mts"]; - -class DatoBuilderCLI { - constructor() { - this.args = process.argv.slice(2); - this.command = this.args[0]; - this.options = this.parseOptions(); - this.ItemTypeBuilder = null; - this.loadItemTypeBuilder(); - } - - /** - * Parse command line options - */ - parseOptions() { - const options = { - concurrency: 1, // Default: sequential - maxConcurrency: 5, // Default max limit - timeout: 30000, // 30 seconds timeout per file - }; - - for (let i = 0; i < this.args.length; i++) { - const arg = this.args[i]; - - if (arg === "--concurrent" || arg === "-c") { - options.concurrency = - parseInt(this.args[i + 1]) || options.maxConcurrency; - this.args.splice(i, 2); - i--; - } else if (arg === "--timeout" || arg === "-t") { - options.timeout = parseInt(this.args[i + 1]) || options.timeout; - this.args.splice(i, 2); - i--; - } else if (arg === "--max-concurrent") { - options.concurrency = options.maxConcurrency; - this.args.splice(i, 1); - i--; - } - } - - return options; - } - - loadItemTypeBuilder() { - try { - this.ItemTypeBuilder = require("../build/ItemTypeBuilder").default; - } catch (error) { - console.error("❌ Failed to load ItemTypeBuilder:", error.message); - process.exit(1); - } - } - - /** - * Check if the project is configured as ESM - */ - isESMProject() { - try { - const pkg = JSON.parse( - fs.readFileSync(path.resolve("package.json"), "utf8"), - ); - return pkg.type === "module"; - } catch { - return false; - } - } - - /** - * Check if file has explicit ESM extension - */ - isESMFile(filePath) { - return ESM_EXTENSIONS.some((ext) => filePath.endsWith(ext)); - } - - /** - * Check if file has TypeScript extension - */ - isTypeScriptFile(filePath) { - return TS_EXTENSIONS.some((ext) => filePath.endsWith(ext)); - } - - /** - * Detect module syntax by analyzing file content - */ - detectModuleSyntax(filePath) { - try { - const content = fs.readFileSync(filePath, "utf8"); - const hasESMSyntax = /^\s*(import|export)\s/m.test(content); - const hasCJSSyntax = /require\s*\(|module\.exports|exports\./m.test( - content, - ); - - if (hasESMSyntax && !hasCJSSyntax) return "esm"; - if (hasCJSSyntax && !hasESMSyntax) return "commonjs"; - if (hasESMSyntax && hasCJSSyntax) return "mixed"; - - return "unknown"; - } catch { - return "unknown"; - } - } - - /** - * Find all runnable files in a directory recursively - */ - findRunnableFiles(dirPath) { - const result = []; - - const walk = (currentPath) => { - try { - const entries = fs.readdirSync(currentPath, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(currentPath, entry.name); - - if (entry.isDirectory()) { - // Skip common directories that shouldn't contain runnable scripts - if ( - !["node_modules", ".git", "dist", "build", ".next"].includes( - entry.name, - ) - ) { - walk(fullPath); - } - } else if (entry.isFile()) { - if (SUPPORTED_EXTENSIONS.some((ext) => entry.name.endsWith(ext))) { - result.push(fullPath); - } - } - } - } catch (error) { - console.warn( - `⚠️ Cannot read directory ${currentPath}: ${error.message}`, - ); - } - }; - - walk(dirPath); - return result.sort(); // Sort for consistent ordering - } - - /** - * Determine the appropriate Node.js execution strategy - */ - getExecutionStrategy(filePath) { - const isTs = this.isTypeScriptFile(filePath); - const isExplicitESM = this.isESMFile(filePath); - const projectIsESM = this.isESMProject(); - const fileSyntax = this.detectModuleSyntax(filePath); - const hasESMSyntax = fileSyntax === "esm"; - const useESM = isExplicitESM || projectIsESM; - - const env = { - ...process.env, - NODE_OPTIONS: "--enable-source-maps", - }; - - let nodeArgs = []; - - if (isTs) { - if (hasESMSyntax && !useESM) { - // TypeScript with ESM syntax but should run as CommonJS - env.TS_NODE_COMPILER_OPTIONS = JSON.stringify({ - module: "CommonJS", - moduleResolution: "node", - esModuleInterop: true, - allowSyntheticDefaultImports: true, - }); - nodeArgs = ["-r", "ts-node/register"]; - } else if (useESM) { - // TypeScript with ESM - nodeArgs = ["--loader", "ts-node/esm"]; - } else { - // TypeScript with CommonJS - nodeArgs = ["-r", "ts-node/register"]; - } - } else { - // JavaScript files - if (useESM && !isExplicitESM) { - nodeArgs = ["--input-type=module"]; - } - // For explicit ESM (.mjs) or regular JS, no special args needed - } - - return { nodeArgs, env }; - } - - /** - * Execute a single file with timeout - */ - async runFile(filePath) { - const { nodeArgs, env } = this.getExecutionStrategy(filePath); - - return new Promise((resolve, reject) => { - const relativePath = path.relative(process.cwd(), filePath); - - // Only log start message in sequential mode to avoid cluttering - if (this.options.concurrency === 1) { - console.log(`πŸš€ Running: ${relativePath}`); - } - - const child = spawn( - "node", - [...nodeArgs, filePath, ...this.args.slice(2)], - { - stdio: this.options.concurrency === 1 ? "inherit" : "pipe", - env, - }, - ); - - let stdout = ""; - let stderr = ""; - - // Capture output for concurrent execution - if (this.options.concurrency > 1) { - child.stdout?.on("data", (data) => { - stdout += data.toString(); - }); - child.stderr?.on("data", (data) => { - stderr += data.toString(); - }); - } - - // Add timeout - const timeout = setTimeout(() => { - child.kill("SIGTERM"); - reject( - new Error(`File execution timed out after ${this.options.timeout}ms`), - ); - }, this.options.timeout); - - child.on("exit", (code) => { - clearTimeout(timeout); - if (code === 0) { - if (this.options.concurrency === 1) { - console.log(`βœ… Completed: ${relativePath}\n`); - } - resolve({ code, stdout, stderr, file: relativePath }); - } else { - const error = new Error(`Process exited with code ${code}`); - error.code = code; - error.stdout = stdout; - error.stderr = stderr; - error.file = relativePath; - reject(error); - } - }); - - child.on("error", (error) => { - clearTimeout(timeout); - error.file = relativePath; - reject(error); - }); - }); - } - - /** - * Execute files in a directory or a single file - */ - async runFileOrDirectory(inputPath) { - const resolvedPath = path.resolve(process.cwd(), inputPath); - - if (!fs.existsSync(resolvedPath)) { - console.error(`❌ File or directory not found: ${resolvedPath}`); - process.exit(1); - } - - const stat = fs.statSync(resolvedPath); - - if (stat.isDirectory()) { - const files = this.findRunnableFiles(resolvedPath); - - if (files.length === 0) { - console.warn(`⚠️ No runnable files found in: ${resolvedPath}`); - return; - } - - console.log(`πŸ“ Found ${files.length} file(s) to run in ${resolvedPath}`); - - // Determine optimal concurrency - const concurrency = - this.options.concurrency > 1 - ? Math.min(this.options.concurrency, files.length) - : 1; - - if (concurrency > 1) { - console.log(`πŸš€ Running with concurrency limit of ${concurrency}\n`); - await this.runFilesWithSmartConcurrency(files, concurrency); - } else { - console.log("πŸš€ Running sequentially\n"); - await this.runFilesSequentially(files); - } - } else if (stat.isFile()) { - if (!SUPPORTED_EXTENSIONS.some((ext) => resolvedPath.endsWith(ext))) { - console.error(`❌ Unsupported file type: ${resolvedPath}`); - console.error( - `Supported extensions: ${SUPPORTED_EXTENSIONS.join(", ")}`, - ); - process.exit(1); - } - - try { - await this.runFile(resolvedPath); - } catch (error) { - console.error(`❌ Failed: ${error.file || resolvedPath}`); - if (error.stderr) { - console.error(error.stderr); - } - process.exit(1); - } - } else { - console.error(`❌ Unsupported path type: ${resolvedPath}`); - process.exit(1); - } - } - - /** - * Run files sequentially (original behavior) - */ - async runFilesSequentially(files) { - let failedCount = 0; - const failedFiles = []; - - for (const file of files) { - try { - await this.runFile(file); - } catch (error) { - failedCount++; - failedFiles.push({ file: error.file || file, error }); - } - } - - if (failedCount > 0) { - console.error(`❌ ${failedCount} file(s) failed to execute:`); - failedFiles.forEach(({ file, error }) => { - console.error(` - ${file}: ${error.message}`); - }); - process.exit(1); - } else { - console.log(`πŸŽ‰ All ${files.length} file(s) executed successfully`); - } - } - - /** - * Smart concurrency with proper error handling and progress tracking - */ - async runFilesWithSmartConcurrency(files, concurrency) { - const startTime = Date.now(); - let completed = 0; - let failed = 0; - const failedFiles = []; - - const updateProgress = () => { - const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); - const total = completed + failed; - process.stdout.write( - `\rπŸ“Š Progress: ${total}/${files.length} processed (${completed} βœ…, ${failed} ❌) - ${elapsed}s`, - ); - }; - - const executeFile = async (file) => { - try { - const result = await this.runFile(file); - completed++; - updateProgress(); - return { success: true, file, result }; - } catch (error) { - failed++; - failedFiles.push({ file: error.file || file, error }); - updateProgress(); - return { success: false, file, error }; - } - }; - - // Use controlled concurrency with a sliding window approach - const executing = new Set(); - const results = []; - - for (const file of files) { - // Wait if we've hit the concurrency limit - while (executing.size >= concurrency) { - await Promise.race([...executing]); - } - - const promise = executeFile(file); - executing.add(promise); - results.push(promise); - - // Remove from executing set when promise resolves - promise.finally(() => { - executing.delete(promise); - }); - } - - // Wait for all remaining promises - await Promise.all(results); - - console.log("\n"); // New line after progress - - const totalTime = ((Date.now() - startTime) / 1000).toFixed(1); - - if (failed > 0) { - console.error( - `❌ ${failed} file(s) failed to execute (completed in ${totalTime}s):`, - ); - failedFiles.forEach(({ file, error }) => { - console.error(` - ${file}: ${error.message}`); - if (error.stderr) { - console.error(` ${error.stderr.trim()}`); - } - }); - process.exit(1); - } else { - console.log( - `πŸŽ‰ All ${files.length} file(s) executed successfully in ${totalTime}s`, - ); - const avgSpeed = (files.length / parseFloat(totalTime)).toFixed(1); - console.log(`⚑ Average: ${avgSpeed} files/second`); - } - } - - /** - * Clear the ItemTypeBuilder cache - */ - clearCache() { - try { - this.ItemTypeBuilder.clearCache(); - console.log("βœ… Cache cleared successfully"); - } catch (error) { - console.error("❌ Failed to clear cache:", error.message); - process.exit(1); - } - } - - /** - * Display help information - */ - showHelp() { - console.log("πŸ“¦ Dato Builder CLI"); - console.log(""); - console.log("Usage:"); - console.log( - " dato-builder run [options] Run a file or all files in a directory", - ); - console.log( - " dato-builder clear-cache Clear the ItemTypeBuilder cache", - ); - console.log( - " dato-builder help Show this help message", - ); - console.log(""); - console.log("Options:"); - console.log( - " -c, --concurrent Run files concurrently (default: 1)", - ); - console.log( - " --max-concurrent Run with maximum concurrency (5)", - ); - console.log( - " -t, --timeout Timeout per file in milliseconds (default: 30000)", - ); - console.log(""); - console.log("Supported file types:"); - console.log(` ${SUPPORTED_EXTENSIONS.join(", ")}`); - console.log(""); - console.log("Examples:"); - console.log(" dato-builder run ./scripts/build.ts"); - console.log(" dato-builder run ./scripts/ --concurrent 3"); - console.log(" dato-builder run ./scripts/ --max-concurrent"); - console.log(" dato-builder run ./scripts/ --timeout 60000"); - console.log(" dato-builder clear-cache"); - } - - /** - * Main CLI entry point - */ - async run() { - try { - switch (this.command) { - case "run": { - const input = this.args[1]; - if (!input) { - console.error("❌ Missing path argument"); - console.error("Usage: dato-builder run "); - process.exit(1); - } - await this.runFileOrDirectory(input); - break; - } - - case "clear-cache": - this.clearCache(); - break; - - case "help": - case "--help": - case "-h": - this.showHelp(); - break; - - default: - if (this.command) { - console.error(`❌ Unknown command: ${this.command}`); - console.error("Run 'dato-builder help' for usage information"); - process.exit(1); - } else { - this.showHelp(); - } - } - } catch (error) { - console.error("❌ CLI Error:", error.message); - process.exit(1); - } - } -} - -// Execute CLI -if (require.main === module) { - const cli = new DatoBuilderCLI(); - cli.run().catch((error) => { - console.error("❌ Unexpected error:", error); - process.exit(1); - }); -} - -module.exports = DatoBuilderCLI; +setupCLI(); diff --git a/biome.jsonc b/biome.jsonc index 66cc312..816eb8f 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -6,7 +6,8 @@ "useIgnoreFile": true }, "files": { - "ignoreUnknown": false + "ignoreUnknown": false, + "includes": ["**"] }, "formatter": { "enabled": true, @@ -21,18 +22,32 @@ }, "linter": { "enabled": true, + "domains": { + "project": "recommended", + "test": "recommended" + }, "rules": { "recommended": true, + "complexity": { + "noForEach": "warn" + }, "correctness": { "noUnusedImports": "error", "noUnusedVariables": "error", "noUnusedFunctionParameters": "error" }, "style": { + "noProcessEnv": "warn", "noUnusedTemplateLiteral": "error" }, "nursery": { - "useSortedClasses": "error" + "noUnassignedVariables": "error", + "useReadonlyClassProperties": "warn", + "noMisusedPromises": "error", + "noMagicNumbers": "warn", + "noImplicitCoercion": "error", + "useSortedClasses": "warn", + "useUnifiedTypeSignature": "warn" } } }, diff --git a/jest.config.js b/jest.config.js index 304a65d..191a622 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,23 @@ /** @type {import('ts-jest').JestConfigWithTsJest} **/ module.exports = { + preset: "ts-jest", testEnvironment: "node", - transform: { - "^.+.tsx?$": ["ts-jest", {}], + moduleNameMapper: { + "^@/(.*)$": "/src/$1", + "^@tests/(.*)$": "/tests/$1", }, + moduleDirectories: ["node_modules", "src", "tests"], + roots: ["/src", "/tests"], + collectCoverageFrom: [ + "src/**/*.{ts,tsx}", + "!src/**/*.d.ts", + "!src/**/*.test.ts", + "!src/datocms/.generated/**/*", + ], + coverageDirectory: "coverage", + coverageReporters: ["text", "lcov", "html"], + testMatch: ["**/__tests__/**/*.(ts|tsx|js)", "**/*.(test|spec).(ts|tsx|js)"], + clearMocks: true, + restoreMocks: true, + maxWorkers: "50%", }; diff --git a/lefthook.yml b/lefthook.yml index f5a8560..0c7b78c 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -5,12 +5,20 @@ commit-msg: "spellcheck-commit-msg.sh": runner: bash +output: + - meta + - summary + - empty_summary + - success + - failure + - skips + pre-commit: parallel: true commands: check: glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,json,jsonc}" - run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files} + run: npx @biomejs/biome check --write --no-errors-on-unmatched --diagnostic-level=error --files-ignore-unknown=true --colors=off {staged_files} stage_fixed: true cspell: diff --git a/package-lock.json b/package-lock.json index b3c1aa6..41bbcdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13699 +1,15381 @@ { - "name": "dato-builder", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "dato-builder", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@datocms/cma-client-node": "^4.0.1", - "pluralize": "^8.0.0", - "tsconfig-paths": "^4.2.0" - }, - "bin": { - "dato-builder": "bin/cli.js" - }, - "devDependencies": { - "@biomejs/biome": "2.1.1", - "@commitlint/cli": "^19.8.0", - "@commitlint/config-conventional": "^19.8.0", - "@cspell/dict-software-terms": "^5.0.7", - "@jest/globals": "^30.0.3", - "@semantic-release/changelog": "^6.0.3", - "@semantic-release/git": "^10.0.1", - "@semantic-release/npm": "^12.0.1", - "@types/node": "^24.0.8", - "@types/pluralize": "^0.0.33", - "cspell": "^9.0.0", - "jest": "^30.0.3", - "lefthook": "^1.11.11", - "nodemon": "^3.1.10", - "rimraf": "^6.0.1", - "ts-jest": "^29.3.2", - "ts-node": "^10.9.2", - "typescript": "^5.8.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.7.tgz", - "integrity": "sha512-xgu/ySj2mTiUFmdE9yCMfBxLp4DHd5DwmbbD05YAuICfodYT3VvRxbrh81LGQ/8UpSdtMdfKMn3KouYDX59DGQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.7.tgz", - "integrity": "sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.27.7", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.7", - "@babel/types": "^7.27.7", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.7.tgz", - "integrity": "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.27.7" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.7.tgz", - "integrity": "sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.5", - "@babel/parser": "^7.27.7", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.7.tgz", - "integrity": "sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@biomejs/biome": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.1.tgz", - "integrity": "sha512-HFGYkxG714KzG+8tvtXCJ1t1qXQMzgWzfvQaUjxN6UeKv+KvMEuliInnbZLJm6DXFXwqVi6446EGI0sGBLIYng==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.1.1", - "@biomejs/cli-darwin-x64": "2.1.1", - "@biomejs/cli-linux-arm64": "2.1.1", - "@biomejs/cli-linux-arm64-musl": "2.1.1", - "@biomejs/cli-linux-x64": "2.1.1", - "@biomejs/cli-linux-x64-musl": "2.1.1", - "@biomejs/cli-win32-arm64": "2.1.1", - "@biomejs/cli-win32-x64": "2.1.1" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.1.tgz", - "integrity": "sha512-2Muinu5ok4tWxq4nu5l19el48cwCY/vzvI7Vjbkf3CYIQkjxZLyj0Ad37Jv2OtlXYaLvv+Sfu1hFeXt/JwRRXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.1.tgz", - "integrity": "sha512-cC8HM5lrgKQXLAK+6Iz2FrYW5A62pAAX6KAnRlEyLb+Q3+Kr6ur/sSuoIacqlp1yvmjHJqjYfZjPvHWnqxoEIA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.1.tgz", - "integrity": "sha512-tw4BEbhAUkWPe4WBr6IX04DJo+2jz5qpPzpW/SWvqMjb9QuHY8+J0M23V8EPY/zWU4IG8Ui0XESapR1CB49Q7g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.1.tgz", - "integrity": "sha512-/7FBLnTswu4jgV9ttI3AMIdDGqVEPIZd8I5u2D4tfCoj8rl9dnjrEQbAIDlWhUXdyWlFSz8JypH3swU9h9P+2A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.1.tgz", - "integrity": "sha512-3WJ1GKjU7NzZb6RTbwLB59v9cTIlzjbiFLDB0z4376TkDqoNYilJaC37IomCr/aXwuU8QKkrYoHrgpSq5ffJ4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.1.tgz", - "integrity": "sha512-kUu+loNI3OCD2c12cUt7M5yaaSjDnGIksZwKnueubX6c/HWUyi/0mPbTBHR49Me3F0KKjWiKM+ZOjsmC+lUt9g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.1.tgz", - "integrity": "sha512-vEHK0v0oW+E6RUWLoxb2isI3rZo57OX9ZNyyGH701fZPj6Il0Rn1f5DMNyCmyflMwTnIQstEbs7n2BxYSqQx4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.1.tgz", - "integrity": "sha512-i2PKdn70kY++KEF/zkQFvQfX1e8SkA8hq4BgC+yE9dZqyLzB/XStY2MvwI3qswlRgnGpgncgqe0QYKVS1blksg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@commitlint/cli": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.8.1.tgz", - "integrity": "sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/format": "^19.8.1", - "@commitlint/lint": "^19.8.1", - "@commitlint/load": "^19.8.1", - "@commitlint/read": "^19.8.1", - "@commitlint/types": "^19.8.1", - "tinyexec": "^1.0.0", - "yargs": "^17.0.0" - }, - "bin": { - "commitlint": "cli.js" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/config-conventional": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.1.tgz", - "integrity": "sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^19.8.1", - "conventional-changelog-conventionalcommits": "^7.0.2" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/config-validator": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz", - "integrity": "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^19.8.1", - "ajv": "^8.11.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/ensure": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.8.1.tgz", - "integrity": "sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^19.8.1", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.upperfirst": "^4.3.1" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/execute-rule": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.1.tgz", - "integrity": "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/format": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.8.1.tgz", - "integrity": "sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^19.8.1", - "chalk": "^5.3.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/format/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@commitlint/is-ignored": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.8.1.tgz", - "integrity": "sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^19.8.1", - "semver": "^7.6.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/lint": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.8.1.tgz", - "integrity": "sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/is-ignored": "^19.8.1", - "@commitlint/parse": "^19.8.1", - "@commitlint/rules": "^19.8.1", - "@commitlint/types": "^19.8.1" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/load": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.1.tgz", - "integrity": "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/config-validator": "^19.8.1", - "@commitlint/execute-rule": "^19.8.1", - "@commitlint/resolve-extends": "^19.8.1", - "@commitlint/types": "^19.8.1", - "chalk": "^5.3.0", - "cosmiconfig": "^9.0.0", - "cosmiconfig-typescript-loader": "^6.1.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/load/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@commitlint/message": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.8.1.tgz", - "integrity": "sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/parse": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.8.1.tgz", - "integrity": "sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^19.8.1", - "conventional-changelog-angular": "^7.0.0", - "conventional-commits-parser": "^5.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/read": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.8.1.tgz", - "integrity": "sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/top-level": "^19.8.1", - "@commitlint/types": "^19.8.1", - "git-raw-commits": "^4.0.0", - "minimist": "^1.2.8", - "tinyexec": "^1.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/resolve-extends": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz", - "integrity": "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/config-validator": "^19.8.1", - "@commitlint/types": "^19.8.1", - "global-directory": "^4.0.1", - "import-meta-resolve": "^4.0.0", - "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/rules": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.8.1.tgz", - "integrity": "sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/ensure": "^19.8.1", - "@commitlint/message": "^19.8.1", - "@commitlint/to-lines": "^19.8.1", - "@commitlint/types": "^19.8.1" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/to-lines": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.8.1.tgz", - "integrity": "sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/top-level": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.8.1.tgz", - "integrity": "sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^7.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/top-level/node_modules/find-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", - "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^7.2.0", - "path-exists": "^5.0.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/@commitlint/top-level/node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/types": { - "version": "19.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", - "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/conventional-commits-parser": "^5.0.0", - "chalk": "^5.3.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/types/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@cspell/cspell-bundled-dicts": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.1.3.tgz", - "integrity": "sha512-WbOkD32fjxz0hHMP6oTvAgi2VBlzYcqKPNwCo+4b9HefLWV5aiLaxp04d8CeifaAdlYjkjuqRTJXh/HfUeLCVg==", - "dev": true, - "dependencies": { - "@cspell/dict-ada": "^4.1.0", - "@cspell/dict-al": "^1.1.0", - "@cspell/dict-aws": "^4.0.11", - "@cspell/dict-bash": "^4.2.0", - "@cspell/dict-companies": "^3.2.1", - "@cspell/dict-cpp": "^6.0.8", - "@cspell/dict-cryptocurrencies": "^5.0.4", - "@cspell/dict-csharp": "^4.0.6", - "@cspell/dict-css": "^4.0.17", - "@cspell/dict-dart": "^2.3.0", - "@cspell/dict-data-science": "^2.0.8", - "@cspell/dict-django": "^4.1.4", - "@cspell/dict-docker": "^1.1.14", - "@cspell/dict-dotnet": "^5.0.9", - "@cspell/dict-elixir": "^4.0.7", - "@cspell/dict-en_us": "^4.4.13", - "@cspell/dict-en-common-misspellings": "^2.1.2", - "@cspell/dict-en-gb-mit": "^3.1.3", - "@cspell/dict-filetypes": "^3.0.12", - "@cspell/dict-flutter": "^1.1.0", - "@cspell/dict-fonts": "^4.0.4", - "@cspell/dict-fsharp": "^1.1.0", - "@cspell/dict-fullstack": "^3.2.6", - "@cspell/dict-gaming-terms": "^1.1.1", - "@cspell/dict-git": "^3.0.6", - "@cspell/dict-golang": "^6.0.22", - "@cspell/dict-google": "^1.0.8", - "@cspell/dict-haskell": "^4.0.5", - "@cspell/dict-html": "^4.0.11", - "@cspell/dict-html-symbol-entities": "^4.0.3", - "@cspell/dict-java": "^5.0.11", - "@cspell/dict-julia": "^1.1.0", - "@cspell/dict-k8s": "^1.0.11", - "@cspell/dict-kotlin": "^1.1.0", - "@cspell/dict-latex": "^4.0.3", - "@cspell/dict-lorem-ipsum": "^4.0.4", - "@cspell/dict-lua": "^4.0.7", - "@cspell/dict-makefile": "^1.0.4", - "@cspell/dict-markdown": "^2.0.11", - "@cspell/dict-monkeyc": "^1.0.10", - "@cspell/dict-node": "^5.0.7", - "@cspell/dict-npm": "^5.2.9", - "@cspell/dict-php": "^4.0.14", - "@cspell/dict-powershell": "^5.0.14", - "@cspell/dict-public-licenses": "^2.0.13", - "@cspell/dict-python": "^4.2.18", - "@cspell/dict-r": "^2.1.0", - "@cspell/dict-ruby": "^5.0.8", - "@cspell/dict-rust": "^4.0.11", - "@cspell/dict-scala": "^5.0.7", - "@cspell/dict-shell": "^1.1.0", - "@cspell/dict-software-terms": "^5.1.2", - "@cspell/dict-sql": "^2.2.0", - "@cspell/dict-svelte": "^1.0.6", - "@cspell/dict-swift": "^2.0.5", - "@cspell/dict-terraform": "^1.1.2", - "@cspell/dict-typescript": "^3.2.2", - "@cspell/dict-vue": "^3.0.4" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/cspell-json-reporter": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.1.3.tgz", - "integrity": "sha512-FvzlSQuU+bNeo77v0KrA/lkoe324cHvZNhkx7Dtp1aj01FeBr5Y36gozR3DNY6tewBi6hC7uLeeNg/iSBf6CWg==", - "dev": true, - "dependencies": { - "@cspell/cspell-types": "9.1.3" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/cspell-pipe": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.1.3.tgz", - "integrity": "sha512-Cns37ml7IaXMWBci9XOqdTkP9nFtOO8+sJ4VvtbVO68Zo8v0vq74ApDbPgGI2HzYtn7Jj2hxQqGIBdLnmrMPyA==", - "dev": true, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/cspell-resolver": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.1.3.tgz", - "integrity": "sha512-3h9AkbY+YutBG91fQxeSpfIRT50sfrNQ7IAS0N6fCvJ6z0sXed7UPYwf90NauQp/1lN/bVlHFFAgxDEyG720Yg==", - "dev": true, - "dependencies": { - "global-directory": "^4.0.1" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/cspell-service-bus": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.1.3.tgz", - "integrity": "sha512-Ss4cCnkJI3IHDSOQKxhtAfypvZZDzuJeXbZFVimLvO22/8GdVH+vQxAFm3kBY+ACVUAe13MQIYzZxuFHaM9y8g==", - "dev": true, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/cspell-types": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.1.3.tgz", - "integrity": "sha512-JPLFMp6qKj4fjsEDvMjVXFZg+j3HaRQ7raFtR2RPidYyKcUHPCVhX0wfJ0vuYxkC0Yst+99tgVxR8Wi57xs2Ew==", - "dev": true, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/dict-ada": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.1.0.tgz", - "integrity": "sha512-7SvmhmX170gyPd+uHXrfmqJBY5qLcCX8kTGURPVeGxmt8XNXT75uu9rnZO+jwrfuU2EimNoArdVy5GZRGljGNg==", - "dev": true - }, - "node_modules/@cspell/dict-al": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.1.0.tgz", - "integrity": "sha512-PtNI1KLmYkELYltbzuoztBxfi11jcE9HXBHCpID2lou/J4VMYKJPNqe4ZjVzSI9NYbMnMnyG3gkbhIdx66VSXg==", - "dev": true - }, - "node_modules/@cspell/dict-aws": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.11.tgz", - "integrity": "sha512-nesbrYbxP/ek7Nc3X1ENWxAXJ/2XIKGxauF0k4VSPLtMvWP50gHAEe+zmqFciFolwIVVjF2l+juDdUdBMPbMiw==", - "dev": true - }, - "node_modules/@cspell/dict-bash": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.0.tgz", - "integrity": "sha512-HOyOS+4AbCArZHs/wMxX/apRkjxg6NDWdt0jF9i9XkvJQUltMwEhyA2TWYjQ0kssBsnof+9amax2lhiZnh3kCg==", - "dev": true, - "dependencies": { - "@cspell/dict-shell": "1.1.0" - } - }, - "node_modules/@cspell/dict-companies": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.1.tgz", - "integrity": "sha512-ryaeJ1KhTTKL4mtinMtKn8wxk6/tqD4vX5tFP+Hg89SiIXmbMk5vZZwVf+eyGUWJOyw5A1CVj9EIWecgoi+jYQ==", - "dev": true - }, - "node_modules/@cspell/dict-cpp": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.8.tgz", - "integrity": "sha512-BzurRZilWqaJt32Gif6/yCCPi+FtrchjmnehVEIFzbWyeBd/VOUw77IwrEzehZsu5cRU91yPWuWp5fUsKfDAXA==", - "dev": true - }, - "node_modules/@cspell/dict-cryptocurrencies": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.4.tgz", - "integrity": "sha512-6iFu7Abu+4Mgqq08YhTKHfH59mpMpGTwdzDB2Y8bbgiwnGFCeoiSkVkgLn1Kel2++hYcZ8vsAW/MJS9oXxuMag==", - "dev": true - }, - "node_modules/@cspell/dict-csharp": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.6.tgz", - "integrity": "sha512-w/+YsqOknjQXmIlWDRmkW+BHBPJZ/XDrfJhZRQnp0wzpPOGml7W0q1iae65P2AFRtTdPKYmvSz7AL5ZRkCnSIw==", - "dev": true - }, - "node_modules/@cspell/dict-css": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.17.tgz", - "integrity": "sha512-2EisRLHk6X/PdicybwlajLGKF5aJf4xnX2uuG5lexuYKt05xV/J/OiBADmi8q9obhxf1nesrMQbqAt+6CsHo/w==", - "dev": true - }, - "node_modules/@cspell/dict-dart": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.0.tgz", - "integrity": "sha512-1aY90lAicek8vYczGPDKr70pQSTQHwMFLbmWKTAI6iavmb1fisJBS1oTmMOKE4ximDf86MvVN6Ucwx3u/8HqLg==", - "dev": true - }, - "node_modules/@cspell/dict-data-science": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.8.tgz", - "integrity": "sha512-uyAtT+32PfM29wRBeAkUSbkytqI8bNszNfAz2sGPtZBRmsZTYugKMEO9eDjAIE/pnT9CmbjNuoiXhk+Ss4fCOg==", - "dev": true - }, - "node_modules/@cspell/dict-django": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.4.tgz", - "integrity": "sha512-fX38eUoPvytZ/2GA+g4bbdUtCMGNFSLbdJJPKX2vbewIQGfgSFJKY56vvcHJKAvw7FopjvgyS/98Ta9WN1gckg==", - "dev": true - }, - "node_modules/@cspell/dict-docker": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.14.tgz", - "integrity": "sha512-p6Qz5mokvcosTpDlgSUREdSbZ10mBL3ndgCdEKMqjCSZJFdfxRdNdjrGER3lQ6LMq5jGr1r7nGXA0gvUJK80nw==", - "dev": true - }, - "node_modules/@cspell/dict-dotnet": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.9.tgz", - "integrity": "sha512-JGD6RJW5sHtO5lfiJl11a5DpPN6eKSz5M1YBa1I76j4dDOIqgZB6rQexlDlK1DH9B06X4GdDQwdBfnpAB0r2uQ==", - "dev": true - }, - "node_modules/@cspell/dict-elixir": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.7.tgz", - "integrity": "sha512-MAUqlMw73mgtSdxvbAvyRlvc3bYnrDqXQrx5K9SwW8F7fRYf9V4vWYFULh+UWwwkqkhX9w03ZqFYRTdkFku6uA==", - "dev": true - }, - "node_modules/@cspell/dict-en_us": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.13.tgz", - "integrity": "sha512-6TEHCJKmRqq7fQI7090p+ju12vhuGcNkc6YfxHrcjO816m53VPVaS6IfG6+6OqelQiOMjr0ZD8IHcDIkwThSFw==", - "dev": true - }, - "node_modules/@cspell/dict-en-common-misspellings": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.2.tgz", - "integrity": "sha512-r74AObInM1XOUxd3lASnNZNDOIA9Bka7mBDTkvkOeCGoLQhn+Cr7h1889u4K07KHbecKMHP6zw5zQhkdocNzCw==", - "dev": true - }, - "node_modules/@cspell/dict-en-gb-mit": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.3.tgz", - "integrity": "sha512-4aY8ySQxSNSRILtf9lJIfSR+su86u8VL6z41gOIhvLIvYnHMFiohV7ebM91GbtdZXBazL7zmGFcpm2EnBzewug==", - "dev": true - }, - "node_modules/@cspell/dict-filetypes": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.12.tgz", - "integrity": "sha512-+ds5wgNdlUxuJvhg8A1TjuSpalDFGCh7SkANCWvIplg6QZPXL4j83lqxP7PgjHpx7PsBUS7vw0aiHPjZy9BItw==", - "dev": true - }, - "node_modules/@cspell/dict-flutter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-flutter/-/dict-flutter-1.1.0.tgz", - "integrity": "sha512-3zDeS7zc2p8tr9YH9tfbOEYfopKY/srNsAa+kE3rfBTtQERAZeOhe5yxrnTPoufctXLyuUtcGMUTpxr3dO0iaA==", - "dev": true - }, - "node_modules/@cspell/dict-fonts": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.4.tgz", - "integrity": "sha512-cHFho4hjojBcHl6qxidl9CvUb492IuSk7xIf2G2wJzcHwGaCFa2o3gRcxmIg1j62guetAeDDFELizDaJlVRIOg==", - "dev": true - }, - "node_modules/@cspell/dict-fsharp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.1.0.tgz", - "integrity": "sha512-oguWmHhGzgbgbEIBKtgKPrFSVAFtvGHaQS0oj+vacZqMObwkapcTGu7iwf4V3Bc2T3caf0QE6f6rQfIJFIAVsw==", - "dev": true - }, - "node_modules/@cspell/dict-fullstack": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.6.tgz", - "integrity": "sha512-cSaq9rz5RIU9j+0jcF2vnKPTQjxGXclntmoNp4XB7yFX2621PxJcekGjwf/lN5heJwVxGLL9toR0CBlGKwQBgA==", - "dev": true - }, - "node_modules/@cspell/dict-gaming-terms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.1.1.tgz", - "integrity": "sha512-tb8GFxjTLDQstkJcJ90lDqF4rKKlMUKs5/ewePN9P+PYRSehqDpLI5S5meOfPit8LGszeOrjUdBQ4zXo7NpMyQ==", - "dev": true - }, - "node_modules/@cspell/dict-git": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.6.tgz", - "integrity": "sha512-nazfOqyxlBOQGgcur9ssEOEQCEZkH8vXfQe8SDEx8sCN/g0SFm8ktabgLVmBOXjy3RzjVNLlM2nBfRQ7e6+5hQ==", - "dev": true - }, - "node_modules/@cspell/dict-golang": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.22.tgz", - "integrity": "sha512-FvV0m3Y0nUFxw36uDCD8UtfOPv4wsZnnlabNwB3xNZ2IBn0gBURuMUZywScb9sd2wXM8VFBRoU//tc6NQsOVOg==", - "dev": true - }, - "node_modules/@cspell/dict-google": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-google/-/dict-google-1.0.8.tgz", - "integrity": "sha512-BnMHgcEeaLyloPmBs8phCqprI+4r2Jb8rni011A8hE+7FNk7FmLE3kiwxLFrcZnnb7eqM0agW4zUaNoB0P+z8A==", - "dev": true - }, - "node_modules/@cspell/dict-haskell": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.5.tgz", - "integrity": "sha512-s4BG/4tlj2pPM9Ha7IZYMhUujXDnI0Eq1+38UTTCpatYLbQqDwRFf2KNPLRqkroU+a44yTUAe0rkkKbwy4yRtQ==", - "dev": true - }, - "node_modules/@cspell/dict-html": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.11.tgz", - "integrity": "sha512-QR3b/PB972SRQ2xICR1Nw/M44IJ6rjypwzA4jn+GH8ydjAX9acFNfc+hLZVyNe0FqsE90Gw3evLCOIF0vy1vQw==", - "dev": true - }, - "node_modules/@cspell/dict-html-symbol-entities": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.3.tgz", - "integrity": "sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A==", - "dev": true - }, - "node_modules/@cspell/dict-java": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.11.tgz", - "integrity": "sha512-T4t/1JqeH33Raa/QK/eQe26FE17eUCtWu+JsYcTLkQTci2dk1DfcIKo8YVHvZXBnuM43ATns9Xs0s+AlqDeH7w==", - "dev": true - }, - "node_modules/@cspell/dict-julia": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-julia/-/dict-julia-1.1.0.tgz", - "integrity": "sha512-CPUiesiXwy3HRoBR3joUseTZ9giFPCydSKu2rkh6I2nVjXnl5vFHzOMLXpbF4HQ1tH2CNfnDbUndxD+I+7eL9w==", - "dev": true - }, - "node_modules/@cspell/dict-k8s": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.11.tgz", - "integrity": "sha512-8ojNwB5j4PfZ1Gq9n5c/HKJCtZD3h6+wFy+zpALpDWFFQ2qT22Be30+3PVd+G5gng8or0LeK8VgKKd0l1uKPTA==", - "dev": true - }, - "node_modules/@cspell/dict-kotlin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-kotlin/-/dict-kotlin-1.1.0.tgz", - "integrity": "sha512-vySaVw6atY7LdwvstQowSbdxjXG6jDhjkWVWSjg1XsUckyzH1JRHXe9VahZz1i7dpoFEUOWQrhIe5B9482UyJQ==", - "dev": true - }, - "node_modules/@cspell/dict-latex": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.3.tgz", - "integrity": "sha512-2KXBt9fSpymYHxHfvhUpjUFyzrmN4c4P8mwIzweLyvqntBT3k0YGZJSriOdjfUjwSygrfEwiuPI1EMrvgrOMJw==", - "dev": true - }, - "node_modules/@cspell/dict-lorem-ipsum": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.4.tgz", - "integrity": "sha512-+4f7vtY4dp2b9N5fn0za/UR0kwFq2zDtA62JCbWHbpjvO9wukkbl4rZg4YudHbBgkl73HRnXFgCiwNhdIA1JPw==", - "dev": true - }, - "node_modules/@cspell/dict-lua": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.7.tgz", - "integrity": "sha512-Wbr7YSQw+cLHhTYTKV6cAljgMgcY+EUAxVIZW3ljKswEe4OLxnVJ7lPqZF5JKjlXdgCjbPSimsHqyAbC5pQN/Q==", - "dev": true - }, - "node_modules/@cspell/dict-makefile": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.4.tgz", - "integrity": "sha512-E4hG/c0ekPqUBvlkrVvzSoAA+SsDA9bLi4xSV3AXHTVru7Y2bVVGMPtpfF+fI3zTkww/jwinprcU1LSohI3ylw==", - "dev": true - }, - "node_modules/@cspell/dict-markdown": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.11.tgz", - "integrity": "sha512-stZieFKJyMQbzKTVoalSx2QqCpB0j8nPJF/5x+sBnDIWgMC65jp8Wil+jccWh9/vnUVukP3Ejewven5NC7SWuQ==", - "dev": true, - "peerDependencies": { - "@cspell/dict-css": "^4.0.17", - "@cspell/dict-html": "^4.0.11", - "@cspell/dict-html-symbol-entities": "^4.0.3", - "@cspell/dict-typescript": "^3.2.2" - } - }, - "node_modules/@cspell/dict-monkeyc": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.10.tgz", - "integrity": "sha512-7RTGyKsTIIVqzbvOtAu6Z/lwwxjGRtY5RkKPlXKHEoEAgIXwfDxb5EkVwzGQwQr8hF/D3HrdYbRT8MFBfsueZw==", - "dev": true - }, - "node_modules/@cspell/dict-node": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.7.tgz", - "integrity": "sha512-ZaPpBsHGQCqUyFPKLyCNUH2qzolDRm1/901IO8e7btk7bEDF56DN82VD43gPvD4HWz3yLs/WkcLa01KYAJpnOw==", - "dev": true - }, - "node_modules/@cspell/dict-npm": { - "version": "5.2.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.10.tgz", - "integrity": "sha512-MGR5S5e/0zcX3ln4eXQNYs3HBkX/JciqAmnCS0mNVx2jic1TtWBxJx+a33+95OhZeWF2Z/qL63FUQqZrJL27VA==", - "dev": true - }, - "node_modules/@cspell/dict-php": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.14.tgz", - "integrity": "sha512-7zur8pyncYZglxNmqsRycOZ6inpDoVd4yFfz1pQRe5xaRWMiK3Km4n0/X/1YMWhh3e3Sl/fQg5Axb2hlN68t1g==", - "dev": true - }, - "node_modules/@cspell/dict-powershell": { - "version": "5.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.14.tgz", - "integrity": "sha512-ktjjvtkIUIYmj/SoGBYbr3/+CsRGNXGpvVANrY0wlm/IoGlGywhoTUDYN0IsGwI2b8Vktx3DZmQkfb3Wo38jBA==", - "dev": true - }, - "node_modules/@cspell/dict-public-licenses": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.13.tgz", - "integrity": "sha512-1Wdp/XH1ieim7CadXYE7YLnUlW0pULEjVl9WEeziZw3EKCAw8ZI8Ih44m4bEa5VNBLnuP5TfqC4iDautAleQzQ==", - "dev": true - }, - "node_modules/@cspell/dict-python": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.18.tgz", - "integrity": "sha512-hYczHVqZBsck7DzO5LumBLJM119a3F17aj8a7lApnPIS7cmEwnPc2eACNscAHDk7qAo2127oI7axUoFMe9/g1g==", - "dev": true, - "dependencies": { - "@cspell/dict-data-science": "^2.0.8" - } - }, - "node_modules/@cspell/dict-r": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.1.0.tgz", - "integrity": "sha512-k2512wgGG0lTpTYH9w5Wwco+lAMf3Vz7mhqV8+OnalIE7muA0RSuD9tWBjiqLcX8zPvEJr4LdgxVju8Gk3OKyA==", - "dev": true - }, - "node_modules/@cspell/dict-ruby": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.8.tgz", - "integrity": "sha512-ixuTneU0aH1cPQRbWJvtvOntMFfeQR2KxT8LuAv5jBKqQWIHSxzGlp+zX3SVyoeR0kOWiu64/O5Yn836A5yMcQ==", - "dev": true - }, - "node_modules/@cspell/dict-rust": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.11.tgz", - "integrity": "sha512-OGWDEEzm8HlkSmtD8fV3pEcO2XBpzG2XYjgMCJCRwb2gRKvR+XIm6Dlhs04N/K2kU+iH8bvrqNpM8fS/BFl0uw==", - "dev": true - }, - "node_modules/@cspell/dict-scala": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.7.tgz", - "integrity": "sha512-yatpSDW/GwulzO3t7hB5peoWwzo+Y3qTc0pO24Jf6f88jsEeKmDeKkfgPbYuCgbE4jisGR4vs4+jfQZDIYmXPA==", - "dev": true - }, - "node_modules/@cspell/dict-shell": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.1.0.tgz", - "integrity": "sha512-D/xHXX7T37BJxNRf5JJHsvziFDvh23IF/KvkZXNSh8VqcRdod3BAz9VGHZf6VDqcZXr1VRqIYR3mQ8DSvs3AVQ==", - "dev": true - }, - "node_modules/@cspell/dict-software-terms": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.3.tgz", - "integrity": "sha512-kHQmiMvAuXvF54S1uLZNVUJatnDv8L+pRnPMAiFXPTdudi6oM04TzI8yj8anm7gLcpfmJLCIECnc3s+we5eeXw==", - "dev": true - }, - "node_modules/@cspell/dict-sql": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.2.0.tgz", - "integrity": "sha512-MUop+d1AHSzXpBvQgQkCiok8Ejzb+nrzyG16E8TvKL2MQeDwnIvMe3bv90eukP6E1HWb+V/MA/4pnq0pcJWKqQ==", - "dev": true - }, - "node_modules/@cspell/dict-svelte": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.6.tgz", - "integrity": "sha512-8LAJHSBdwHCoKCSy72PXXzz7ulGROD0rP1CQ0StOqXOOlTUeSFaJJlxNYjlONgd2c62XBQiN2wgLhtPN+1Zv7Q==", - "dev": true - }, - "node_modules/@cspell/dict-swift": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.5.tgz", - "integrity": "sha512-3lGzDCwUmnrfckv3Q4eVSW3sK3cHqqHlPprFJZD4nAqt23ot7fic5ALR7J4joHpvDz36nHX34TgcbZNNZOC/JA==", - "dev": true - }, - "node_modules/@cspell/dict-terraform": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.1.2.tgz", - "integrity": "sha512-RB9dnhxKIiWpwQB+b3JuFa8X4m+6Ny92Y4Z5QARR7jEtapg8iF2ODZX1yLtozp4kFVoRsUKEP6vj3MLv87VTdg==", - "dev": true - }, - "node_modules/@cspell/dict-typescript": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.2.tgz", - "integrity": "sha512-H9Y+uUHsTIDFO/jdfUAcqmcd5osT+2DB5b0aRCHfLWN/twUbGn/1qq3b7YwEvttxKlYzWHU3uNFf+KfA93VY7w==", - "dev": true - }, - "node_modules/@cspell/dict-vue": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.4.tgz", - "integrity": "sha512-0dPtI0lwHcAgSiQFx8CzvqjdoXROcH+1LyqgROCpBgppommWpVhbQ0eubnKotFEXgpUCONVkeZJ6Ql8NbTEu+w==", - "dev": true - }, - "node_modules/@cspell/dynamic-import": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.1.3.tgz", - "integrity": "sha512-+8PxTslsh+oTxmhYdnfQZ/brYGFAnfqLR9xotWE4Ks3HoaLOhZsp6FF9kvlEp/gNOjpyhHn1UhT/Gr5fT4+QhQ==", - "dev": true, - "dependencies": { - "@cspell/url": "9.1.3", - "import-meta-resolve": "^4.1.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/filetypes": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.1.3.tgz", - "integrity": "sha512-HRJEggDo6OJJmCc/gq7oriMqkqVDema+oLpGBh1a/M7ulw+CzoHkOa//1ohpAJh5KsWj9Tej9Va4BUZ/SaCwUA==", - "dev": true, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/strong-weak-map": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.1.3.tgz", - "integrity": "sha512-+96SI9R6TOY+xGBOK5LiOgX/W/9gAKus1Cvngh2LdtDVZwgVqpqvm5LoXxLhUT+Vs5UsndRBzblSdNpziSwZtA==", - "dev": true, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspell/url": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.1.3.tgz", - "integrity": "sha512-LQQKY0O4QYUNKyDod8VfEBvqeJNGHJlx1v0gDq00eMvaClnkIz+y2ObGdtDlF7ZbG7TgI6PQ3ahJdlqfRPe3ZQ==", - "dev": true, - "engines": { - "node": ">=20" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@datocms/cma-client": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@datocms/cma-client/-/cma-client-4.0.2.tgz", - "integrity": "sha512-AhtI7hGZxjHcScE+Wkk01BFgkiL+tNft0oMDyuKlVEKF0zRCqRHipwf8lwfO5zQP4XFQ6Qn4Fh/B8oF30q/BIA==", - "dependencies": { - "@datocms/rest-client-utils": "^4.0.2", - "uuid": "^9.0.1" - } - }, - "node_modules/@datocms/cma-client-node": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@datocms/cma-client-node/-/cma-client-node-4.0.2.tgz", - "integrity": "sha512-tiduimUbtH28Uc5gcBZu5QriMXdjb1vnw60hp6n5CKicVDQTm7703W3yp0fVWyDCW4gKLS66rfKzAobPn9zgyA==", - "dependencies": { - "@datocms/cma-client": "^4.0.2", - "@datocms/rest-client-utils": "^4.0.2", - "mime-types": "^2.1.35", - "tmp-promise": "^3.0.3" - } - }, - "node_modules/@datocms/rest-client-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@datocms/rest-client-utils/-/rest-client-utils-4.0.2.tgz", - "integrity": "sha512-aKg8bl9mHebWSnBgX1f3Y9hGkX0b+fHlEmhTvZ2JqzrVcu7RZjEWe612RFpyuTISv3hgIRbgmcE7KDfJe/WnfA==", - "dependencies": { - "async-scheduler": "^1.4.4" - } - }, - "node_modules/@emnapi/core": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", - "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.3", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", - "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", - "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.4.tgz", - "integrity": "sha512-tMLCDvBJBwPqMm4OAiuKm2uF5y5Qe26KgcMn+nrDSWpEW+eeFmqA0iO4zJfL16GP7gE3bUUQ3hIuUJ22AqVRnw==", - "dev": true, - "dependencies": { - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.0.2", - "jest-util": "30.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.4.tgz", - "integrity": "sha512-MWScSO9GuU5/HoWjpXAOBs6F/iobvK1XlioelgOM9St7S0Z5WTI9kjCQLPeo4eQRRYusyLW25/J7J5lbFkrYXw==", - "dev": true, - "dependencies": { - "@jest/console": "30.0.4", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.0.2", - "jest-config": "30.0.4", - "jest-haste-map": "30.0.2", - "jest-message-util": "30.0.2", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.2", - "jest-resolve-dependencies": "30.0.4", - "jest-runner": "30.0.4", - "jest-runtime": "30.0.4", - "jest-snapshot": "30.0.4", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "jest-watcher": "30.0.4", - "micromatch": "^4.0.8", - "pretty-format": "30.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.4.tgz", - "integrity": "sha512-5NT+sr7ZOb8wW7C4r7wOKnRQ8zmRWQT2gW4j73IXAKp5/PX1Z8MCStBLQDYfIG3n1Sw0NRfYGdp0iIPVooBAFQ==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "jest-mock": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.4.tgz", - "integrity": "sha512-Z/DL7t67LBHSX4UzDyeYKqOxE/n7lbrrgEwWM3dGiH5Dgn35nk+YtgzKudmfIrBI8DRRrKYY5BCo3317HZV1Fw==", - "dev": true, - "dependencies": { - "expect": "30.0.4", - "jest-snapshot": "30.0.4" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.4.tgz", - "integrity": "sha512-EgXecHDNfANeqOkcak0DxsoVI4qkDUsR7n/Lr2vtmTBjwLPBnnPOF71S11Q8IObWzxm2QgQoY6f9hzrRD3gHRA==", - "dev": true, - "dependencies": { - "@jest/get-type": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.4.tgz", - "integrity": "sha512-qZ7nxOcL5+gwBO6LErvwVy5k06VsX/deqo2XnVUSTV0TNC9lrg8FC3dARbi+5lmrr5VyX5drragK+xLcOjvjYw==", - "dev": true, - "dependencies": { - "@jest/types": "30.0.1", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.0.2", - "jest-mock": "30.0.2", - "jest-util": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", - "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", - "dev": true, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.4.tgz", - "integrity": "sha512-avyZuxEHF2EUhFF6NEWVdxkRRV6iXXcIES66DLhuLlU7lXhtFG/ySq/a8SRZmEJSsLkNAFX6z6mm8KWyXe9OEA==", - "dev": true, - "dependencies": { - "@jest/environment": "30.0.4", - "@jest/expect": "30.0.4", - "@jest/types": "30.0.1", - "jest-mock": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.4.tgz", - "integrity": "sha512-6ycNmP0JSJEEys1FbIzHtjl9BP0tOZ/KN6iMeAKrdvGmUsa1qfRdlQRUDKJ4P84hJ3xHw1yTqJt4fvPNHhyE+g==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.0.2", - "jest-util": "30.0.2", - "jest-worker": "30.0.2", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@jest/reporters/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.1.tgz", - "integrity": "sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.4.tgz", - "integrity": "sha512-BEpX8M/Y5lG7MI3fmiO+xCnacOrVsnbqVrcDZIT8aSGkKV1w2WwvRQxSWw5SIS8ozg7+h8tSj5EO1Riqqxcdag==", - "dev": true, - "dependencies": { - "@jest/types": "30.0.1", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jest/test-result": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.4.tgz", - "integrity": "sha512-Mfpv8kjyKTHqsuu9YugB6z1gcdB3TSSOaKlehtVaiNlClMkEHY+5ZqCY2CrEE3ntpBMlstX/ShDAf84HKWsyIw==", - "dev": true, - "dependencies": { - "@jest/console": "30.0.4", - "@jest/types": "30.0.1", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.4.tgz", - "integrity": "sha512-bj6ePmqi4uxAE8EHE0Slmk5uBYd9Vd/PcVt06CsBxzH4bbA8nGsI1YbXl/NH+eii4XRtyrRx+Cikub0x8H4vDg==", - "dev": true, - "dependencies": { - "@jest/test-result": "30.0.4", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.4.tgz", - "integrity": "sha512-atvy4hRph/UxdCIBp+UB2jhEA/jJiUeGZ7QPgBi9jUUKNgi3WEoMXGNG7zbbELG2+88PMabUNCDchmqgJy3ELg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.0.1", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.0", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.2", - "jest-regex-util": "30.0.1", - "jest-util": "30.0.2", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jest/types": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.1.tgz", - "integrity": "sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==", - "dev": true, - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.1", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", - "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@octokit/auth-token": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", - "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", - "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.2.2", - "@octokit/request": "^9.2.3", - "@octokit/request-error": "^6.1.8", - "@octokit/types": "^14.0.0", - "before-after-hook": "^3.0.2", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/endpoint": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", - "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/types": "^14.0.0", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/graphql": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", - "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/request": "^9.2.3", - "@octokit/types": "^14.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", - "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-12.0.0.tgz", - "integrity": "sha512-MPd6WK1VtZ52lFrgZ0R2FlaoiWllzgqFHaSZxvp72NmoDeZ0m8GeJdg4oB6ctqMTYyrnDYp592Xma21mrgiyDA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/types": "^14.0.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-retry": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz", - "integrity": "sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/request-error": "^6.1.8", - "@octokit/types": "^14.0.0", - "bottleneck": "^2.15.3" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-throttling": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-10.0.0.tgz", - "integrity": "sha512-Kuq5/qs0DVYTHZuBAzCZStCzo2nKvVRo/TDNhCcpC2TKiOGz/DisXMCvjt3/b5kr6SCI1Y8eeeJTHBxxpFvZEg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/types": "^14.0.0", - "bottleneck": "^2.15.3" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": "^6.1.3" - } - }, - "node_modules/@octokit/request": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", - "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/endpoint": "^10.1.4", - "@octokit/request-error": "^6.1.8", - "@octokit/types": "^14.0.0", - "fast-content-type-parse": "^2.0.0", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/request-error": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", - "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/types": "^14.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", - "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/openapi-types": "^25.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", - "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true, - "license": "ISC" - }, - "node_modules/@pnpm/npm-conf": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", - "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@semantic-release/changelog": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", - "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "fs-extra": "^11.0.0", - "lodash": "^4.17.4" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0" - } - }, - "node_modules/@semantic-release/commit-analyzer": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz", - "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "conventional-changelog-angular": "^8.0.0", - "conventional-changelog-writer": "^8.0.0", - "conventional-commits-filter": "^5.0.0", - "conventional-commits-parser": "^6.0.0", - "debug": "^4.0.0", - "import-from-esm": "^2.0.0", - "lodash-es": "^4.17.21", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=20.8.1" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-changelog-angular": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", - "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.1.0.tgz", - "integrity": "sha512-5nxDo7TwKB5InYBl4ZC//1g9GRwB/F3TXOGR9hgUjMGfvSP4Vu5NkpNro2+1+TIEy1vwxApl5ircECr2ri5JIw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "meow": "^13.0.0" - }, - "bin": { - "conventional-commits-parser": "dist/cli/index.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@semantic-release/git": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", - "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "execa": "^5.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.0", - "p-reduce": "^2.0.0" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0" - } - }, - "node_modules/@semantic-release/github": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.2.tgz", - "integrity": "sha512-EhHimj3/eOSPu0OflgDzwgrawoGJIn8XLOkNS6WzwuTr8ebxyX976Y4mCqJ8MlkdQpV5+8T+49sy8xXlcm6uCg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@octokit/core": "^6.0.0", - "@octokit/plugin-paginate-rest": "^12.0.0", - "@octokit/plugin-retry": "^7.0.0", - "@octokit/plugin-throttling": "^10.0.0", - "@semantic-release/error": "^4.0.0", - "aggregate-error": "^5.0.0", - "debug": "^4.3.4", - "dir-glob": "^3.0.1", - "globby": "^14.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "issue-parser": "^7.0.0", - "lodash-es": "^4.17.21", - "mime": "^4.0.0", - "p-filter": "^4.0.0", - "url-join": "^5.0.0" - }, - "engines": { - "node": ">=20.8.1" - }, - "peerDependencies": { - "semantic-release": ">=24.1.0" - } - }, - "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", - "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@semantic-release/github/node_modules/aggregate-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", - "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "clean-stack": "^5.2.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/github/node_modules/clean-stack": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", - "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/github/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/github/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.2.tgz", - "integrity": "sha512-+M9/Lb35IgnlUO6OSJ40Ie+hUsZLuph2fqXC/qrKn0fMvUU/jiCjpoL6zEm69vzcmaZJ8yNKtMBEKHWN49WBbQ==", - "dev": true, - "dependencies": { - "@semantic-release/error": "^4.0.0", - "aggregate-error": "^5.0.0", - "execa": "^9.0.0", - "fs-extra": "^11.0.0", - "lodash-es": "^4.17.21", - "nerf-dart": "^1.0.0", - "normalize-url": "^8.0.0", - "npm": "^10.9.3", - "rc": "^1.2.8", - "read-pkg": "^9.0.0", - "registry-auth-token": "^5.0.0", - "semver": "^7.1.2", - "tempy": "^3.0.0" - }, - "engines": { - "node": ">=20.8.1" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", - "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@semantic-release/npm/node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/aggregate-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", - "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^5.2.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/clean-stack": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", - "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/execa": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", - "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.3", - "figures": "^6.1.0", - "get-stream": "^9.0.0", - "human-signals": "^8.0.0", - "is-plain-obj": "^4.1.0", - "is-stream": "^4.0.1", - "npm-run-path": "^6.0.0", - "pretty-ms": "^9.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.5.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/@semantic-release/npm/node_modules/get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/human-signals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", - "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm-run-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", - "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz", - "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "conventional-changelog-angular": "^8.0.0", - "conventional-changelog-writer": "^8.0.0", - "conventional-commits-filter": "^5.0.0", - "conventional-commits-parser": "^6.0.0", - "debug": "^4.0.0", - "get-stream": "^7.0.0", - "import-from-esm": "^2.0.0", - "into-stream": "^7.0.0", - "lodash-es": "^4.17.21", - "read-package-up": "^11.0.0" - }, - "engines": { - "node": ">=20.8.1" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", - "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.1.0.tgz", - "integrity": "sha512-5nxDo7TwKB5InYBl4ZC//1g9GRwB/F3TXOGR9hgUjMGfvSP4Vu5NkpNro2+1+TIEy1vwxApl5ircECr2ri5JIw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "meow": "^13.0.0" - }, - "bin": { - "conventional-commits-parser": "dist/cli/index.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", - "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.37", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.37.tgz", - "integrity": "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==", - "dev": true - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/conventional-commits-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", - "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/node": { - "version": "24.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz", - "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", - "dev": true, - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/pluralize": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.33.tgz", - "integrity": "sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.0.tgz", - "integrity": "sha512-LRw5BW29sYj9NsQC6QoqeLVQhEa+BwVINYyMlcve+6stwdBsSt5UB7zw4UZB4+4PNqIVilHoMaPWCb/KhABHQw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.0.tgz", - "integrity": "sha512-zYX8D2zcWCAHqghA8tPjbp7LwjVXbIZP++mpU/Mrf5jUVlk3BWIxkeB8yYzZi5GpFSlqMcRZQxQqbMI0c2lASQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.0.tgz", - "integrity": "sha512-YsYOT049hevAY/lTYD77GhRs885EXPeAfExG5KenqMJ417nYLS2N/kpRpYbABhFZBVQn+2uRPasTe4ypmYoo3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.0.tgz", - "integrity": "sha512-PSjvk3OZf1aZImdGY5xj9ClFG3bC4gnSSYWrt+id0UAv+GwwVldhpMFjAga8SpMo2T1GjV9UKwM+QCsQCQmtdA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.0.tgz", - "integrity": "sha512-KC/iFaEN/wsTVYnHClyHh5RSYA9PpuGfqkFua45r4sweXpC0KHZ+BYY7ikfcGPt5w1lMpR1gneFzuqWLQxsRKg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.0.tgz", - "integrity": "sha512-CDh/0v8uot43cB4yKtDL9CVY8pbPnMV0dHyQCE4lFz6PW/+9tS0i9eqP5a91PAqEBVMqH1ycu+k8rP6wQU846w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.0.tgz", - "integrity": "sha512-+TE7epATDSnvwr3L/hNHX3wQ8KQYB+jSDTdywycg3qDqvavRP8/HX9qdq/rMcnaRDn4EOtallb3vL/5wCWGCkw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.0.tgz", - "integrity": "sha512-VBAYGg3VahofpQ+L4k/ZO8TSICIbUKKTaMYOWHWfuYBFqPbSkArZZLezw3xd27fQkxX4BaLGb/RKnW0dH9Y/UA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.0.tgz", - "integrity": "sha512-9IgGFUUb02J1hqdRAHXpZHIeUHRrbnGo6vrRbz0fREH7g+rzQy53/IBSyadZ/LG5iqMxukriNPu4hEMUn+uWEg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.0.tgz", - "integrity": "sha512-LR4iQ/LPjMfivpL2bQ9kmm3UnTas3U+umcCnq/CV7HAkukVdHxrDD1wwx74MIWbbgzQTLPYY7Ur2MnnvkYJCBQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.0.tgz", - "integrity": "sha512-HCupFQwMrRhrOg7YHrobbB5ADg0Q8RNiuefqMHVsdhEy9lLyXm/CxsCXeLJdrg27NAPsCaMDtdlm8Z2X8x91Tg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.0.tgz", - "integrity": "sha512-Ckxy76A5xgjWa4FNrzcKul5qFMWgP5JSQ5YKd0XakmWOddPLSkQT+uAvUpQNnFGNbgKzv90DyQlxPDYPQ4nd6A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.0.tgz", - "integrity": "sha512-HfO0PUCCRte2pMJmVyxPI+eqT7KuV3Fnvn2RPvMe5mOzb2BJKf4/Vth8sSt9cerQboMaTVpbxyYjjLBWIuI5BQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.0.tgz", - "integrity": "sha512-9PZdjP7tLOEjpXHS6+B/RNqtfVUyDEmaViPOuSqcbomLdkJnalt5RKQ1tr2m16+qAufV0aDkfhXtoO7DQos/jg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.0.tgz", - "integrity": "sha512-qkE99ieiSKMnFJY/EfyGKVtNra52/k+lVF/PbO4EL5nU6AdvG4XhtJ+WHojAJP7ID9BNIra/yd75EHndewNRfA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.0.tgz", - "integrity": "sha512-MjXek8UL9tIX34gymvQLecz2hMaQzOlaqYJJBomwm1gsvK2F7hF+YqJJ2tRyBDTv9EZJGMt4KlKkSD/gZWCOiw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.0.tgz", - "integrity": "sha512-9LT6zIGO7CHybiQSh7DnQGwFMZvVr0kUjah6qQfkH2ghucxPV6e71sUXJdSM4Ba0MaGE6DC/NwWf7mJmc3DAng==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.0.tgz", - "integrity": "sha512-HYchBYOZ7WN266VjoGm20xFv5EonG/ODURRgwl9EZT7Bq1nLEs6VKJddzfFdXEAho0wfFlt8L/xIiE29Pmy1RA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.0.tgz", - "integrity": "sha512-+oLKLHw3I1UQo4MeHfoLYF+e6YBa8p5vYUw3Rgt7IDzCs+57vIZqQlIo62NDpYM0VG6BjWOwnzBczMvbtH8hag==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/argv-formatter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true, - "license": "MIT" - }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-scheduler": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/async-scheduler/-/async-scheduler-1.4.4.tgz", - "integrity": "sha512-KVWlF6WKlUGJA8FOJYVVk7xDm3PxITWXp9hTeDsXMtUPvItQ2x6g2rIeNAa0TtAiiWvHJqhyA3wo+pj0rA7Ldg==" - }, - "node_modules/babel-jest": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.4.tgz", - "integrity": "sha512-UjG2j7sAOqsp2Xua1mS/e+ekddkSu3wpf4nZUSvXNHuVWdaOUXQ77+uyjJLDE9i0atm5x4kds8K9yb5lRsRtcA==", - "dev": true, - "dependencies": { - "@jest/transform": "30.0.4", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.0", - "babel-preset-jest": "30.0.1", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", - "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", - "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", - "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "30.0.1", - "babel-preset-current-node-syntax": "^1.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/before-after-hook": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", - "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", - "dev": true, - "license": "Apache-2.0", - "peer": true - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001726", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", - "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk-template": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz", - "integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, - "node_modules/chalk-template/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", - "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", - "dev": true - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/clear-module": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", - "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", - "dev": true, - "dependencies": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-highlight": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", - "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "chalk": "^4.0.0", - "highlight.js": "^10.7.1", - "mz": "^2.4.0", - "parse5": "^5.1.1", - "parse5-htmlparser2-tree-adapter": "^6.0.0", - "yargs": "^16.0.0" - }, - "bin": { - "highlight": "bin/highlight" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" - } - }, - "node_modules/cli-highlight/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-highlight/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cli-highlight/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cli-highlight/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/cli-highlight/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-highlight/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-highlight/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/cli-highlight/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cli-highlight/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "license": "ISC", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", - "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/comment-json": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", - "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", - "dev": true, - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, - "node_modules/conventional-changelog-angular": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", - "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", - "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/conventional-changelog-writer": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.1.tgz", - "integrity": "sha512-hlqcy3xHred2gyYg/zXSMXraY2mjAYYo0msUCpK+BGyaVJMFCKWVXPIHiaacGO2GGp13kvHWXFhYmxT4QQqW3Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "conventional-commits-filter": "^5.0.0", - "handlebars": "^4.7.7", - "meow": "^13.0.0", - "semver": "^7.5.2" - }, - "bin": { - "conventional-changelog-writer": "dist/cli/index.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/conventional-changelog-writer/node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conventional-commits-filter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", - "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.mjs" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/convert-hrtime": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", - "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", - "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "jiti": "^2.4.1" - }, - "engines": { - "node": ">=v18" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=9", - "typescript": ">=5" - } - }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cspell": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.1.3.tgz", - "integrity": "sha512-QxpQn9rGIZN/neMU4hx9T4s9AL5nyRhumNCdYHjjU8Pi4ztZOzuVWbOQD1Oq5ygb92Aci76/DwbJQ1dmb4631Q==", - "dev": true, - "dependencies": { - "@cspell/cspell-json-reporter": "9.1.3", - "@cspell/cspell-pipe": "9.1.3", - "@cspell/cspell-types": "9.1.3", - "@cspell/dynamic-import": "9.1.3", - "@cspell/url": "9.1.3", - "chalk": "^5.4.1", - "chalk-template": "^1.1.0", - "commander": "^14.0.0", - "cspell-config-lib": "9.1.3", - "cspell-dictionary": "9.1.3", - "cspell-gitignore": "9.1.3", - "cspell-glob": "9.1.3", - "cspell-io": "9.1.3", - "cspell-lib": "9.1.3", - "fast-json-stable-stringify": "^2.1.0", - "file-entry-cache": "^9.1.0", - "semver": "^7.7.2", - "tinyglobby": "^0.2.14" - }, - "bin": { - "cspell": "bin.mjs", - "cspell-esm": "bin.mjs" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" - } - }, - "node_modules/cspell-config-lib": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.1.3.tgz", - "integrity": "sha512-B3DdOTZNIOQahSkOYqaq2fOc8fq/jFkrOFd36kge/GAyEpY2Um/Kp/GQ6caOcev+ju0h3iGaO24OLCx6QJ3YoQ==", - "dev": true, - "dependencies": { - "@cspell/cspell-types": "9.1.3", - "comment-json": "^4.2.5", - "smol-toml": "^1.4.1", - "yaml": "^2.8.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell-dictionary": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.1.3.tgz", - "integrity": "sha512-BXWwYQ64LaSOd7+8TLZax3AeUnTJUuIl+Tl32/dqcVpgDF4P0eAUVE5xap+QZ2rzKRVFjD8r5M6IR2QrA23o0g==", - "dev": true, - "dependencies": { - "@cspell/cspell-pipe": "9.1.3", - "@cspell/cspell-types": "9.1.3", - "cspell-trie-lib": "9.1.3", - "fast-equals": "^5.2.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell-gitignore": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.1.3.tgz", - "integrity": "sha512-yc7Td6L7ZHejm1OzwY/hyfBgyz3gpToMPDyztwbwOdrxXNLRIgDZVPvjVS67XvNf3dv55J19A/8r5Xd7yaV60w==", - "dev": true, - "dependencies": { - "@cspell/url": "9.1.3", - "cspell-glob": "9.1.3", - "cspell-io": "9.1.3" - }, - "bin": { - "cspell-gitignore": "bin.mjs" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell-glob": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.1.3.tgz", - "integrity": "sha512-If7gSgbWlUhLcmNA9zPflWzdUZs4wyRKB/Ze584wrht7zJR4yJm2Rptk2+M8kXEhx3zYS6UGhSL0alPbVAbjgQ==", - "dev": true, - "dependencies": { - "@cspell/url": "9.1.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell-glob/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/cspell-grammar": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.1.3.tgz", - "integrity": "sha512-L1OVY9RyZXPT+qesw0c7aRKTxQIC7nrLKDQ97hRrQhK23hv5Q8o7GVs1S7pXRNZ/oA8V+VNG2CgjLiKnVM2jnw==", - "dev": true, - "dependencies": { - "@cspell/cspell-pipe": "9.1.3", - "@cspell/cspell-types": "9.1.3" - }, - "bin": { - "cspell-grammar": "bin.mjs" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell-io": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.1.3.tgz", - "integrity": "sha512-fdgAVrthOY1pPsBZHWVjEVn6uHMAshj2n75eu2rvUd6EcmMuLR13EcIXHoMcQo/1Az05x2UgG7HuK+0MuRcikQ==", - "dev": true, - "dependencies": { - "@cspell/cspell-service-bus": "9.1.3", - "@cspell/url": "9.1.3" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell-lib": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.1.3.tgz", - "integrity": "sha512-egESsnErAPtC/wuqbHWW28eRKChkg5h+vFQQuZ0iThuOSZ65jeSM0ESOt8W3TH2JD7EGo2pvPED/7rZjjnMIcQ==", - "dev": true, - "dependencies": { - "@cspell/cspell-bundled-dicts": "9.1.3", - "@cspell/cspell-pipe": "9.1.3", - "@cspell/cspell-resolver": "9.1.3", - "@cspell/cspell-types": "9.1.3", - "@cspell/dynamic-import": "9.1.3", - "@cspell/filetypes": "9.1.3", - "@cspell/strong-weak-map": "9.1.3", - "@cspell/url": "9.1.3", - "clear-module": "^4.1.2", - "comment-json": "^4.2.5", - "cspell-config-lib": "9.1.3", - "cspell-dictionary": "9.1.3", - "cspell-glob": "9.1.3", - "cspell-grammar": "9.1.3", - "cspell-io": "9.1.3", - "cspell-trie-lib": "9.1.3", - "env-paths": "^3.0.0", - "fast-equals": "^5.2.2", - "gensequence": "^7.0.0", - "import-fresh": "^3.3.1", - "resolve-from": "^5.0.0", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-uri": "^3.1.0", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell-lib/node_modules/env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cspell-trie-lib": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.1.3.tgz", - "integrity": "sha512-fvI0ede/rPr+SB0zX8le426c5lroNdmMTkl4fFk2e0w5/JZRHIfkuenhWe0MZeb18d1NPRIiLgxoD87zswLynw==", - "dev": true, - "dependencies": { - "@cspell/cspell-pipe": "9.1.3", - "@cspell/cspell-types": "9.1.3", - "gensequence": "^7.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cspell/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/dargs": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", - "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.178", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.178.tgz", - "integrity": "sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/env-ci": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.0.tgz", - "integrity": "sha512-Z8dnwSDbV1XYM9SBF2J0GcNVvmfmfh3a49qddGIROhBoVro6MZVTji15z/sJbQ2ko2ei8n988EU1wzoLU/tF+g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "execa": "^8.0.0", - "java-properties": "^1.0.2" - }, - "engines": { - "node": "^18.17 || >=20.6.1" - } - }, - "node_modules/env-ci/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/env-ci/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/env-ci/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/exit-x": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.4.tgz", - "integrity": "sha512-dDLGjnP2cKbEppxVICxI/Uf4YemmGMPNy0QytCbfafbpYk9AFQsxb8Uyrxii0RPK7FWgLGlSem+07WirwS3cFQ==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "30.0.4", - "@jest/get-type": "30.0.1", - "jest-matcher-utils": "30.0.4", - "jest-message-util": "30.0.2", - "jest-mock": "30.0.2", - "jest-util": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/fast-content-type-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", - "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "peer": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-equals": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", - "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/figures": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", - "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", - "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-versions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", - "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "semver-regex": "^4.0.5", - "super-regex": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", - "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/fs-extra": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-timeout": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", - "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gensequence": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-7.0.0.tgz", - "integrity": "sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-log-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", - "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "0.6.8" - } - }, - "node_modules/git-log-parser/node_modules/split2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "through2": "~2.0.0" - } - }, - "node_modules/git-raw-commits": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", - "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dargs": "^8.0.0", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.mjs" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/glob": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", - "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/global-directory": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", - "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.3", - "ignore": "^7.0.3", - "path-type": "^6.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/path-type": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", - "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/hook-std": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", - "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC", - "peer": true - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", - "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-from-esm": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", - "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "^4.3.4", - "import-meta-resolve": "^4.0.0" - }, - "engines": { - "node": ">=18.20" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/index-to-position": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", - "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/into-stream": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", - "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "text-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/issue-parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", - "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - }, - "engines": { - "node": "^18.17 || >=20.6.1" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", - "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/java-properties": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", - "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/jest": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.4.tgz", - "integrity": "sha512-9QE0RS4WwTj/TtTC4h/eFVmFAhGNVerSB9XpJh8sqaXlP73ILcPcZ7JWjjEtJJe2m8QyBLKKfPQuK+3F+Xij/g==", - "dev": true, - "dependencies": { - "@jest/core": "30.0.4", - "@jest/types": "30.0.1", - "import-local": "^3.2.0", - "jest-cli": "30.0.4" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.2.tgz", - "integrity": "sha512-Ius/iRST9FKfJI+I+kpiDh8JuUlAISnRszF9ixZDIqJF17FckH5sOzKC8a0wd0+D+8em5ADRHA5V5MnfeDk2WA==", - "dev": true, - "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.0.2", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-circus": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.4.tgz", - "integrity": "sha512-o6UNVfbXbmzjYgmVPtSQrr5xFZCtkDZGdTlptYvGFSN80RuOOlTe73djvMrs+QAuSERZWcHBNIOMH+OEqvjWuw==", - "dev": true, - "dependencies": { - "@jest/environment": "30.0.4", - "@jest/expect": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.0.2", - "jest-matcher-utils": "30.0.4", - "jest-message-util": "30.0.2", - "jest-runtime": "30.0.4", - "jest-snapshot": "30.0.4", - "jest-util": "30.0.2", - "p-limit": "^3.1.0", - "pretty-format": "30.0.2", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.4.tgz", - "integrity": "sha512-3dOrP3zqCWBkjoVG1zjYJpD9143N9GUCbwaF2pFF5brnIgRLHmKcCIw+83BvF1LxggfMWBA0gxkn6RuQVuRhIQ==", - "dev": true, - "dependencies": { - "@jest/core": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/types": "30.0.1", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.0.4", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "yargs": "^17.7.2" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.4.tgz", - "integrity": "sha512-3dzbO6sh34thAGEjJIW0fgT0GA0EVlkski6ZzMcbW6dzhenylXAE/Mj2MI4HonroWbkKc6wU6bLVQ8dvBSZ9lA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.0.1", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.0.4", - "@jest/types": "30.0.1", - "babel-jest": "30.0.4", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.0.4", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.4", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.2", - "jest-runner": "30.0.4", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.0.2", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/jest-config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-config/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-config/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/jest-config/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-config/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-diff": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.4.tgz", - "integrity": "sha512-TSjceIf6797jyd+R64NXqicttROD+Qf98fex7CowmlSn7f8+En0da1Dglwr1AXxDtVizoxXYZBlUQwNhoOXkNw==", - "dev": true, - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "pretty-format": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", - "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", - "dev": true, - "dependencies": { - "detect-newline": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-each": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.2.tgz", - "integrity": "sha512-ZFRsTpe5FUWFQ9cWTMguCaiA6kkW5whccPy9JjD1ezxh+mJeqmz8naL8Fl/oSbNJv3rgB0x87WBIkA5CObIUZQ==", - "dev": true, - "dependencies": { - "@jest/get-type": "30.0.1", - "@jest/types": "30.0.1", - "chalk": "^4.1.2", - "jest-util": "30.0.2", - "pretty-format": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.4.tgz", - "integrity": "sha512-p+rLEzC2eThXqiNh9GHHTC0OW5Ca4ZfcURp7scPjYBcmgpR9HG6750716GuUipYf2AcThU3k20B31USuiaaIEg==", - "dev": true, - "dependencies": { - "@jest/environment": "30.0.4", - "@jest/fake-timers": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "jest-mock": "30.0.2", - "jest-util": "30.0.2", - "jest-validate": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.2.tgz", - "integrity": "sha512-telJBKpNLeCb4MaX+I5k496556Y2FiKR/QLZc0+MGBYl4k3OO0472drlV2LUe7c1Glng5HuAu+5GLYp//GpdOQ==", - "dev": true, - "dependencies": { - "@jest/types": "30.0.1", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.0.2", - "jest-worker": "30.0.2", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/jest-leak-detector": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.2.tgz", - "integrity": "sha512-U66sRrAYdALq+2qtKffBLDWsQ/XoNNs2Lcr83sc9lvE/hEpNafJlq2lXCPUBMNqamMECNxSIekLfe69qg4KMIQ==", - "dev": true, - "dependencies": { - "@jest/get-type": "30.0.1", - "pretty-format": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.4.tgz", - "integrity": "sha512-ubCewJ54YzeAZ2JeHHGVoU+eDIpQFsfPQs0xURPWoNiO42LGJ+QGgfSf+hFIRplkZDkhH5MOvuxHKXRTUU3dUQ==", - "dev": true, - "dependencies": { - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "jest-diff": "30.0.4", - "pretty-format": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.2.tgz", - "integrity": "sha512-vXywcxmr0SsKXF/bAD7t7nMamRvPuJkras00gqYeB1V0WllxZrbZ0paRr3XqpFU2sYYjD0qAaG2fRyn/CGZ0aw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.1", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-mock": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.2.tgz", - "integrity": "sha512-PnZOHmqup/9cT/y+pXIVbbi8ID6U1XHRmbvR7MvUy4SLqhCbwpkmXhLbsWbGewHrV5x/1bF7YDjs+x24/QSvFA==", - "dev": true, - "dependencies": { - "@jest/types": "30.0.1", - "@types/node": "*", - "jest-util": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", - "dev": true, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.2.tgz", - "integrity": "sha512-q/XT0XQvRemykZsvRopbG6FQUT6/ra+XV6rPijyjT6D0msOyCvR2A5PlWZLd+fH0U8XWKZfDiAgrUNDNX2BkCw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.2", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.4.tgz", - "integrity": "sha512-EQBYow19B/hKr4gUTn+l8Z+YLlP2X0IoPyp0UydOtrcPbIOYzJ8LKdFd+yrbwztPQvmlBFUwGPPEzHH1bAvFAw==", - "dev": true, - "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.0.4" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.4.tgz", - "integrity": "sha512-mxY0vTAEsowJwvFJo5pVivbCpuu6dgdXRmt3v3MXjBxFly7/lTk3Td0PaMyGOeNQUFmSuGEsGYqhbn7PA9OekQ==", - "dev": true, - "dependencies": { - "@jest/console": "30.0.4", - "@jest/environment": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.4", - "jest-haste-map": "30.0.2", - "jest-leak-detector": "30.0.2", - "jest-message-util": "30.0.2", - "jest-resolve": "30.0.2", - "jest-runtime": "30.0.4", - "jest-util": "30.0.2", - "jest-watcher": "30.0.4", - "jest-worker": "30.0.2", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.4.tgz", - "integrity": "sha512-tUQrZ8+IzoZYIHoPDQEB4jZoPyzBjLjq7sk0KVyd5UPRjRDOsN7o6UlvaGF8ddpGsjznl9PW+KRgWqCNO+Hn7w==", - "dev": true, - "dependencies": { - "@jest/environment": "30.0.4", - "@jest/fake-timers": "30.0.4", - "@jest/globals": "30.0.4", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.2", - "jest-message-util": "30.0.2", - "jest-mock": "30.0.2", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.2", - "jest-snapshot": "30.0.4", - "jest-util": "30.0.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-runtime/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-snapshot": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.4.tgz", - "integrity": "sha512-S/8hmSkeUib8WRUq9pWEb5zMfsOjiYWDWzFzKnjX7eDyKKgimsu9hcmsUEg8a7dPAw8s/FacxsXquq71pDgPjQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.0.4", - "@jest/get-type": "30.0.1", - "@jest/snapshot-utils": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "babel-preset-current-node-syntax": "^1.1.0", - "chalk": "^4.1.2", - "expect": "30.0.4", - "graceful-fs": "^4.2.11", - "jest-diff": "30.0.4", - "jest-matcher-utils": "30.0.4", - "jest-message-util": "30.0.2", - "jest-util": "30.0.2", - "pretty-format": "30.0.2", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-util": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.2.tgz", - "integrity": "sha512-8IyqfKS4MqprBuUpZNlFB5l+WFehc8bfCe1HSZFHzft2mOuND8Cvi9r1musli+u6F3TqanCZ/Ik4H4pXUolZIg==", - "dev": true, - "dependencies": { - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.2.tgz", - "integrity": "sha512-noOvul+SFER4RIvNAwGn6nmV2fXqBq67j+hKGHKGFCmK4ks/Iy1FSrqQNBLGKlu4ZZIRL6Kg1U72N1nxuRCrGQ==", - "dev": true, - "dependencies": { - "@jest/get-type": "30.0.1", - "@jest/types": "30.0.1", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.4.tgz", - "integrity": "sha512-YESbdHDs7aQOCSSKffG8jXqOKFqw4q4YqR+wHYpR5GWEQioGvL0BfbcjvKIvPEM0XGfsfJrka7jJz3Cc3gI4VQ==", - "dev": true, - "dependencies": { - "@jest/test-result": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.0.2", - "string-length": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.2.tgz", - "integrity": "sha512-RN1eQmx7qSLFA+o9pfJKlqViwL5wt+OL3Vff/A+/cPsmuw7NPwfgl33AP+/agRmHzPOFgXviRycR9kYwlcRQXg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.0.2", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/lefthook": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.11.16.tgz", - "integrity": "sha512-NbFZaAJUEiwBv6Npg7TZOCo9Bxh8VUSuBZ55CTulH9roQjknSXQWgGYz9FaHvqVeMBf7Xog2Wk84Ce7gWrWlYw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "lefthook": "bin/index.js" - }, - "optionalDependencies": { - "lefthook-darwin-arm64": "1.11.16", - "lefthook-darwin-x64": "1.11.16", - "lefthook-freebsd-arm64": "1.11.16", - "lefthook-freebsd-x64": "1.11.16", - "lefthook-linux-arm64": "1.11.16", - "lefthook-linux-x64": "1.11.16", - "lefthook-openbsd-arm64": "1.11.16", - "lefthook-openbsd-x64": "1.11.16", - "lefthook-windows-arm64": "1.11.16", - "lefthook-windows-x64": "1.11.16" - } - }, - "node_modules/lefthook-darwin-arm64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.11.16.tgz", - "integrity": "sha512-ThsVyHPexHax7aZYpFbsswk5HPR2ap3zsJ1yE189Y97U+tLEHS+Mq98MqC1U1zFm0Ts42gpGDBx6chIRjx7Uig==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-darwin-x64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.11.16.tgz", - "integrity": "sha512-P3QtUJ/ICX3l+ZJqqKGyovT6Hi2N60Vt8znGglJ+OeCpVMc6H6N0ID37+jHlnyjO9AfL4EkAj70vwYe8Fzfjsw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-freebsd-arm64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.11.16.tgz", - "integrity": "sha512-X0EyVoCg4+nczJDZ0rc0gpZHOrLr4Nk7knoYFKrBnvutuMCs9ixU+tA4sV8pu4KT13gigbXkxjKlIN7QUqFCxg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-freebsd-x64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.11.16.tgz", - "integrity": "sha512-m2nfcpz2i4AdQ9fZqcoTCxAFotqXHYzUThgqcRyMaPHjlpy+ZmVnopppt9MS4GfDDq+dqjka28ZZObE7T1Nb5g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-linux-arm64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.11.16.tgz", - "integrity": "sha512-mXwCXs5J1qyq6QmBXFy2/YYG0sCzz1C0sfnzLdgXoAAmhEFfA4wy/DX/jNbvMPvRLfKHiXtf3eaqjLEYg9CGjQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-linux-x64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.11.16.tgz", - "integrity": "sha512-MW1ClFIk3uTfpOCm+FGW8NZbXWA6Xdf/eUw6//mEVs3Q4EsZ4GYZn0AFRMrsaXxbSZnvAPhtNoEVqRfYWjWKBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-openbsd-arm64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.11.16.tgz", - "integrity": "sha512-6Aqjyc2TkkEpogsUvzLSdMAy/fN6YHlq3XA47N7VNc0Ke0XP/XIImb0zEPsYDjrdNqZbEKoXn+IYkValtS5DXg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-openbsd-x64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.11.16.tgz", - "integrity": "sha512-ierlKlnrUe1ML6F3JFgFllgy71qY1S5I2wOclI3yh2EGykAJIUmdkgz/f0KT1slV2aXeSP+QgBTu496WzZSjIg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-windows-arm64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.11.16.tgz", - "integrity": "sha512-AAjDKWOExTS1XlSvNNIa3YIJbf90SZ5X0NSA7EgHobegadLcLrkl3aX+2zcw+yvpm1AOF0WUZdYxkAHL5MNQOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/lefthook-windows-x64": { - "version": "1.11.16", - "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.11.16.tgz", - "integrity": "sha512-qOEAinMMV5rlf4C0VPSIlPaj5nh2CD4lzAv7+nAUydDiDQcVkkPbiwCaRkSVX509k6SctDCYQhtBnG/bwdREFQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.capitalize": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "dev": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marked": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", - "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/marked-terminal": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", - "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-escapes": "^7.0.0", - "ansi-regex": "^6.1.0", - "chalk": "^5.4.1", - "cli-highlight": "^2.1.11", - "cli-table3": "^0.6.5", - "node-emoji": "^2.2.0", - "supports-hyperlinks": "^3.1.0" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "marked": ">=1 <16" - } - }, - "node_modules/marked-terminal/node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", - "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa" - ], - "license": "MIT", - "peer": true, - "bin": { - "mime": "bin/cli.js" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/napi-postinstall": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", - "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", - "dev": true, - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/nerf-dart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", - "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@sindresorhus/is": "^4.6.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "node_modules/nodemon": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", - "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/normalize-package-data": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", - "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^7.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/normalize-package-data/node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm": { - "version": "10.9.3", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.3.tgz", - "integrity": "sha512-6Eh1u5Q+kIVXeA8e7l2c/HpnFFcwrkt37xDMujD5be1gloWa9p6j3Fsv3mByXXmqJHy+2cElRMML8opNT7xIJQ==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/promise-spawn", - "@npmcli/redact", - "@npmcli/run-script", - "@sigstore/tuf", - "abbrev", - "archy", - "cacache", - "chalk", - "ci-info", - "cli-columns", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "ms", - "node-gyp", - "nopt", - "normalize-package-data", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "semver", - "spdx-expression-parse", - "ssri", - "supports-color", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], - "dev": true, - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^8.0.1", - "@npmcli/config": "^9.0.0", - "@npmcli/fs": "^4.0.0", - "@npmcli/map-workspaces": "^4.0.2", - "@npmcli/package-json": "^6.2.0", - "@npmcli/promise-spawn": "^8.0.2", - "@npmcli/redact": "^3.2.2", - "@npmcli/run-script": "^9.1.0", - "@sigstore/tuf": "^3.1.1", - "abbrev": "^3.0.1", - "archy": "~1.0.0", - "cacache": "^19.0.1", - "chalk": "^5.4.1", - "ci-info": "^4.2.0", - "cli-columns": "^4.0.0", - "fastest-levenshtein": "^1.0.16", - "fs-minipass": "^3.0.3", - "glob": "^10.4.5", - "graceful-fs": "^4.2.11", - "hosted-git-info": "^8.1.0", - "ini": "^5.0.0", - "init-package-json": "^7.0.2", - "is-cidr": "^5.1.1", - "json-parse-even-better-errors": "^4.0.0", - "libnpmaccess": "^9.0.0", - "libnpmdiff": "^7.0.1", - "libnpmexec": "^9.0.1", - "libnpmfund": "^6.0.1", - "libnpmhook": "^11.0.0", - "libnpmorg": "^7.0.0", - "libnpmpack": "^8.0.1", - "libnpmpublish": "^10.0.1", - "libnpmsearch": "^8.0.0", - "libnpmteam": "^7.0.0", - "libnpmversion": "^7.0.0", - "make-fetch-happen": "^14.0.3", - "minimatch": "^9.0.5", - "minipass": "^7.1.1", - "minipass-pipeline": "^1.2.4", - "ms": "^2.1.2", - "node-gyp": "^11.2.0", - "nopt": "^8.1.0", - "normalize-package-data": "^7.0.0", - "npm-audit-report": "^6.0.0", - "npm-install-checks": "^7.1.1", - "npm-package-arg": "^12.0.2", - "npm-pick-manifest": "^10.0.0", - "npm-profile": "^11.0.1", - "npm-registry-fetch": "^18.0.2", - "npm-user-validate": "^3.0.0", - "p-map": "^7.0.3", - "pacote": "^19.0.1", - "parse-conflict-json": "^4.0.0", - "proc-log": "^5.0.0", - "qrcode-terminal": "^0.12.0", - "read": "^4.1.0", - "semver": "^7.7.2", - "spdx-expression-parse": "^4.0.0", - "ssri": "^12.0.0", - "supports-color": "^9.4.0", - "tar": "^6.2.1", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^3.0.0", - "validate-npm-package-name": "^6.0.1", - "which": "^5.0.0", - "write-file-atomic": "^6.0.0" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/agent": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "8.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^4.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/metavuln-calculator": "^8.0.0", - "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^6.0.1", - "@npmcli/query": "^4.0.0", - "@npmcli/redact": "^3.0.0", - "@npmcli/run-script": "^9.0.1", - "bin-links": "^5.0.0", - "cacache": "^19.0.1", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^8.0.0", - "json-parse-even-better-errors": "^4.0.0", - "json-stringify-nice": "^1.1.4", - "lru-cache": "^10.2.2", - "minimatch": "^9.0.4", - "nopt": "^8.0.0", - "npm-install-checks": "^7.1.0", - "npm-package-arg": "^12.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.1", - "pacote": "^19.0.0", - "parse-conflict-json": "^4.0.0", - "proc-log": "^5.0.0", - "proggy": "^3.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^3.0.1", - "read-package-json-fast": "^4.0.0", - "semver": "^7.3.7", - "ssri": "^12.0.0", - "treeverse": "^3.0.0", - "walk-up-path": "^3.0.1" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "9.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/package-json": "^6.0.1", - "ci-info": "^4.0.0", - "ini": "^5.0.0", - "nopt": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "6.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^8.0.0", - "ini": "^5.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^10.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "4.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^3.0.0", - "@npmcli/package-json": "^6.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "8.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^19.0.0", - "json-parse-even-better-errors": "^4.0.0", - "pacote": "^20.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { - "version": "20.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^6.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^6.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^9.0.0", - "cacache": "^19.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^12.0.0", - "npm-packlist": "^9.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^3.0.0", - "ssri": "^12.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "bin/index.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "6.2.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^6.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^8.0.0", - "json-parse-even-better-errors": "^4.0.0", - "proc-log": "^5.0.0", - "semver": "^7.5.3", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "8.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "which": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/redact": { - "version": "3.2.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "9.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^6.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "node-gyp": "^11.0.0", - "proc-log": "^5.0.0", - "which": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.4.3", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.4.1", - "tuf-js": "^3.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/abbrev": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/agent-base": { - "version": "7.1.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^7.0.0", - "npm-normalize-package-bin": "^4.0.0", - "proc-log": "^5.0.0", - "read-cmd-shim": "^5.0.0", - "write-file-atomic": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "2.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm/node_modules/cacache": { - "version": "19.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^4.0.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^12.0.0", - "tar": "^7.4.3", - "unique-filename": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/chownr": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/tar": { - "version": "7.4.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/cacache/node_modules/yallist": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/chalk": { - "version": "5.4.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ci-info": { - "version": "4.2.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "4.1.3", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "ip-regex": "^5.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/debug": { - "version": "4.4.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/diff": { - "version": "5.2.0", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/npm/node_modules/eastasianwidth": { - "version": "0.2.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/exponential-backoff": { - "version": "3.1.2", - "dev": true, - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.16", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/npm/node_modules/foreground-child": { - "version": "3.3.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "3.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/glob": { - "version": "10.4.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "8.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.2.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "7.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "7.0.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/npm/node_modules/ini": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/init-package-json": { - "version": "7.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^6.0.0", - "npm-package-arg": "^12.0.0", - "promzard": "^2.0.0", - "read": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/ip-address": { - "version": "9.0.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/npm/node_modules/ip-regex": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/is-cidr": { - "version": "5.1.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^4.1.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/jackspeak": { - "version": "3.4.3", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/npm/node_modules/jsbn": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff": { - "version": "6.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.5.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "9.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^12.0.0", - "npm-registry-fetch": "^18.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "7.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^8.0.1", - "@npmcli/installed-package-contents": "^3.0.0", - "binary-extensions": "^2.3.0", - "diff": "^5.1.0", - "minimatch": "^9.0.4", - "npm-package-arg": "^12.0.0", - "pacote": "^19.0.0", - "tar": "^6.2.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "9.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^8.0.1", - "@npmcli/run-script": "^9.0.1", - "ci-info": "^4.0.0", - "npm-package-arg": "^12.0.0", - "pacote": "^19.0.0", - "proc-log": "^5.0.0", - "read": "^4.0.0", - "read-package-json-fast": "^4.0.0", - "semver": "^7.3.7", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "6.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^8.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmhook": { - "version": "11.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "8.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^8.0.1", - "@npmcli/run-script": "^9.0.1", - "npm-package-arg": "^12.0.0", - "pacote": "^19.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "10.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ci-info": "^4.0.0", - "normalize-package-data": "^7.0.0", - "npm-package-arg": "^12.0.0", - "npm-registry-fetch": "^18.0.1", - "proc-log": "^5.0.0", - "semver": "^7.3.7", - "sigstore": "^3.0.0", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^18.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^18.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^6.0.1", - "@npmcli/run-script": "^9.0.1", - "json-parse-even-better-errors": "^4.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "14.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^3.0.0", - "cacache": "^19.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/minipass": { - "version": "7.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-collect": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^3.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minizlib": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp": { - "version": "11.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^14.0.3", - "nopt": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "tar": "^7.4.3", - "tinyglobby": "^0.2.12", - "which": "^5.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/tar": { - "version": "7.4.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/npm/node_modules/nopt": { - "version": "8.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^8.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "7.1.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "12.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "9.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^7.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "10.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^7.1.0", - "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^12.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-profile": { - "version": "11.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^18.0.0", - "proc-log": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "18.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/redact": "^3.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^14.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minizlib": "^3.0.1", - "npm-package-arg": "^12.0.0", - "proc-log": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/p-map": { - "version": "7.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/package-json-from-dist": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/npm/node_modules/pacote": { - "version": "19.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^6.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^6.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^9.0.0", - "cacache": "^19.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^12.0.0", - "npm-packlist": "^9.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^3.0.0", - "ssri": "^12.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "bin/index.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^4.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/path-scurry": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/proc-log": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/proggy": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/promzard": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/npm/node_modules/read": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mute-stream": "^2.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true - }, - "node_modules/npm/node_modules/semver": { - "version": "7.7.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/sigstore": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "@sigstore/sign": "^3.1.0", - "@sigstore/tuf": "^3.1.0", - "@sigstore/verify": "^2.1.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.4.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "make-fetch-happen": "^14.0.2", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks": { - "version": "2.8.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "8.0.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.2.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.5.0", - "dev": true, - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.21", - "dev": true, - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/npm/node_modules/sprintf-js": { - "version": "1.1.3", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause" - }, - "node_modules/npm/node_modules/ssri": { - "version": "12.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/supports-color": { - "version": "9.4.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/npm/node_modules/tar": { - "version": "6.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/minizlib": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tinyglobby": { - "version": "0.2.14", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/npm/node_modules/treeverse": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/tuf-js": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/models": "3.0.1", - "debug": "^4.3.6", - "make-fetch-happen": "^14.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/unique-filename": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/unique-slug": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "6.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/which": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/which/node_modules/isexe": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/npm/node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-each-series": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", - "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-filter": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", - "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-map": "^7.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", - "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", - "dev": true, - "dependencies": { - "callsites": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-ms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pretty-format": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.2.tgz", - "integrity": "sha512-yC5/EBSOrTtqhCKfLHqoUIAXVRZnukHPwWBJWR7h84Q3Be1DRQZLncwcfLoPA5RPQ65qfiCMqgYwdUuQ//eVpg==", - "dev": true, - "dependencies": { - "@jest/schemas": "30.0.1", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-ms": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", - "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse-ms": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true, - "license": "ISC" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "peer": true - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/read-package-up": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", - "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "find-up-simple": "^1.0.0", - "read-pkg": "^9.0.0", - "type-fest": "^4.6.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-package-up/node_modules/type-fest": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.1.tgz", - "integrity": "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "peer": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", - "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.3", - "normalize-package-data": "^6.0.0", - "parse-json": "^8.0.0", - "type-fest": "^4.6.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/parse-json": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", - "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "index-to-position": "^1.1.0", - "type-fest": "^4.39.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.1.tgz", - "integrity": "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/registry-auth-token": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", - "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release": { - "version": "24.2.3", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.3.tgz", - "integrity": "sha512-KRhQG9cUazPavJiJEFIJ3XAMjgfd0fcK3B+T26qOl8L0UG5aZUjeRfREO0KM5InGtYwxqiiytkJrbcYoLDEv0A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@semantic-release/commit-analyzer": "^13.0.0-beta.1", - "@semantic-release/error": "^4.0.0", - "@semantic-release/github": "^11.0.0", - "@semantic-release/npm": "^12.0.0", - "@semantic-release/release-notes-generator": "^14.0.0-beta.1", - "aggregate-error": "^5.0.0", - "cosmiconfig": "^9.0.0", - "debug": "^4.0.0", - "env-ci": "^11.0.0", - "execa": "^9.0.0", - "figures": "^6.0.0", - "find-versions": "^6.0.0", - "get-stream": "^6.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^3.0.0", - "hosted-git-info": "^8.0.0", - "import-from-esm": "^2.0.0", - "lodash-es": "^4.17.21", - "marked": "^12.0.0", - "marked-terminal": "^7.0.0", - "micromatch": "^4.0.2", - "p-each-series": "^3.0.0", - "p-reduce": "^3.0.0", - "read-package-up": "^11.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.3.2", - "semver-diff": "^4.0.0", - "signale": "^1.2.1", - "yargs": "^17.5.1" - }, - "bin": { - "semantic-release": "bin/semantic-release.js" - }, - "engines": { - "node": ">=20.8.1" - } - }, - "node_modules/semantic-release/node_modules/@semantic-release/error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", - "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/semantic-release/node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/aggregate-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", - "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "clean-stack": "^5.2.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/clean-stack": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", - "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/execa": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", - "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.3", - "figures": "^6.1.0", - "get-stream": "^9.0.0", - "human-signals": "^8.0.0", - "is-plain-obj": "^4.1.0", - "is-stream": "^4.0.1", - "npm-run-path": "^6.0.0", - "pretty-ms": "^9.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.5.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/human-signals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", - "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/semantic-release/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/npm-run-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "path-key": "^4.0.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/p-reduce": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", - "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/strip-final-newline": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", - "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semver-regex": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/signale/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/signale/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/signale/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/signale/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/signale/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/signale/node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "unicode-emoji-modifier-base": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/smol-toml": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.1.tgz", - "integrity": "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==", - "dev": true, - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spawn-error-forwarder": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/super-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", - "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "function-timeout": "^1.0.1", - "time-span": "^5.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", - "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=14.18" - }, - "funding": { - "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/synckit": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", - "dev": true, - "dependencies": { - "@pkgr/core": "^0.2.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/temp-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/tempy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", - "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-stream": "^3.0.0", - "temp-dir": "^3.0.0", - "type-fest": "^2.12.2", - "unique-string": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/time-span": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", - "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "convert-hrtime": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", - "license": "MIT", - "dependencies": { - "tmp": "^0.2.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/traverse": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", - "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ts-jest": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", - "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", - "dev": true, - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "optional": true - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true - }, - "node_modules/unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/universal-user-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", - "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", - "dev": true, - "license": "ISC", - "peer": true - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unrs-resolver": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.0.tgz", - "integrity": "sha512-uw3hCGO/RdAEAb4zgJ3C/v6KIAFFOtBoxR86b2Ejc5TnH7HrhTWJR2o0A9ullC3eWMegKQCw/arQ/JivywQzkg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.0", - "@unrs/resolver-binding-android-arm64": "1.11.0", - "@unrs/resolver-binding-darwin-arm64": "1.11.0", - "@unrs/resolver-binding-darwin-x64": "1.11.0", - "@unrs/resolver-binding-freebsd-x64": "1.11.0", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.0", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.0", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.0", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.0", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.0", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.0", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.0", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.0", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.0", - "@unrs/resolver-binding-linux-x64-musl": "1.11.0", - "@unrs/resolver-binding-wasm32-wasi": "1.11.0", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.0", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.0", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/url-join": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", - "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "dev": true - }, - "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", - "dev": true - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "dev": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } + "name": "dato-builder", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dato-builder", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@datocms/cma-client-node": "^4.0.1", + "@inquirer/prompts": "^7.6.0", + "commander": "^14.0.0", + "eslint": "^9.31.0", + "glob": "^11.0.3", + "inquirer": "^12.7.0", + "pluralize": "^8.0.0", + "prettier": "^3.6.2", + "tsx": "^4.20.3" + }, + "bin": { + "dato-builder": "bin/cli.js" + }, + "devDependencies": { + "@biomejs/biome": "2.1.1", + "@commander-js/extra-typings": "^14.0.0", + "@commitlint/cli": "^19.8.0", + "@commitlint/config-conventional": "^19.8.0", + "@cspell/dict-software-terms": "^5.0.7", + "@jest/globals": "^30.0.3", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "@semantic-release/npm": "^12.0.1", + "@types/inquirer": "^9.0.8", + "@types/node": "^24.0.8", + "@types/pluralize": "^0.0.33", + "cspell": "^9.0.0", + "jest": "^30.0.3", + "lefthook": "^1.11.11", + "rimraf": "^6.0.1", + "ts-jest": "^29.3.2", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.7.tgz", + "integrity": "sha512-xgu/ySj2mTiUFmdE9yCMfBxLp4DHd5DwmbbD05YAuICfodYT3VvRxbrh81LGQ/8UpSdtMdfKMn3KouYDX59DGQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.7.tgz", + "integrity": "sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.27.7", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.7", + "@babel/types": "^7.27.7", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.7.tgz", + "integrity": "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.7.tgz", + "integrity": "sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.5", + "@babel/parser": "^7.27.7", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.7.tgz", + "integrity": "sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@biomejs/biome": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.1.tgz", + "integrity": "sha512-HFGYkxG714KzG+8tvtXCJ1t1qXQMzgWzfvQaUjxN6UeKv+KvMEuliInnbZLJm6DXFXwqVi6446EGI0sGBLIYng==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.1.1", + "@biomejs/cli-darwin-x64": "2.1.1", + "@biomejs/cli-linux-arm64": "2.1.1", + "@biomejs/cli-linux-arm64-musl": "2.1.1", + "@biomejs/cli-linux-x64": "2.1.1", + "@biomejs/cli-linux-x64-musl": "2.1.1", + "@biomejs/cli-win32-arm64": "2.1.1", + "@biomejs/cli-win32-x64": "2.1.1" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.1.tgz", + "integrity": "sha512-2Muinu5ok4tWxq4nu5l19el48cwCY/vzvI7Vjbkf3CYIQkjxZLyj0Ad37Jv2OtlXYaLvv+Sfu1hFeXt/JwRRXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.1.tgz", + "integrity": "sha512-cC8HM5lrgKQXLAK+6Iz2FrYW5A62pAAX6KAnRlEyLb+Q3+Kr6ur/sSuoIacqlp1yvmjHJqjYfZjPvHWnqxoEIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.1.tgz", + "integrity": "sha512-tw4BEbhAUkWPe4WBr6IX04DJo+2jz5qpPzpW/SWvqMjb9QuHY8+J0M23V8EPY/zWU4IG8Ui0XESapR1CB49Q7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.1.tgz", + "integrity": "sha512-/7FBLnTswu4jgV9ttI3AMIdDGqVEPIZd8I5u2D4tfCoj8rl9dnjrEQbAIDlWhUXdyWlFSz8JypH3swU9h9P+2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.1.tgz", + "integrity": "sha512-3WJ1GKjU7NzZb6RTbwLB59v9cTIlzjbiFLDB0z4376TkDqoNYilJaC37IomCr/aXwuU8QKkrYoHrgpSq5ffJ4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.1.tgz", + "integrity": "sha512-kUu+loNI3OCD2c12cUt7M5yaaSjDnGIksZwKnueubX6c/HWUyi/0mPbTBHR49Me3F0KKjWiKM+ZOjsmC+lUt9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.1.tgz", + "integrity": "sha512-vEHK0v0oW+E6RUWLoxb2isI3rZo57OX9ZNyyGH701fZPj6Il0Rn1f5DMNyCmyflMwTnIQstEbs7n2BxYSqQx4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.1.tgz", + "integrity": "sha512-i2PKdn70kY++KEF/zkQFvQfX1e8SkA8hq4BgC+yE9dZqyLzB/XStY2MvwI3qswlRgnGpgncgqe0QYKVS1blksg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commander-js/extra-typings": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", + "integrity": "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "commander": "~14.0.0" + } + }, + "node_modules/@commitlint/cli": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.8.1.tgz", + "integrity": "sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^19.8.1", + "@commitlint/lint": "^19.8.1", + "@commitlint/load": "^19.8.1", + "@commitlint/read": "^19.8.1", + "@commitlint/types": "^19.8.1", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.1.tgz", + "integrity": "sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz", + "integrity": "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.8.1.tgz", + "integrity": "sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.1.tgz", + "integrity": "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.8.1.tgz", + "integrity": "sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.8.1.tgz", + "integrity": "sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.8.1.tgz", + "integrity": "sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^19.8.1", + "@commitlint/parse": "^19.8.1", + "@commitlint/rules": "^19.8.1", + "@commitlint/types": "^19.8.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.1.tgz", + "integrity": "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^19.8.1", + "@commitlint/execute-rule": "^19.8.1", + "@commitlint/resolve-extends": "^19.8.1", + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/load/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", + "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "^2.4.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/@commitlint/load/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@commitlint/message": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.8.1.tgz", + "integrity": "sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.8.1.tgz", + "integrity": "sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.8.1", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.8.1.tgz", + "integrity": "sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^19.8.1", + "@commitlint/types": "^19.8.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz", + "integrity": "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^19.8.1", + "@commitlint/types": "^19.8.1", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.8.1.tgz", + "integrity": "sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^19.8.1", + "@commitlint/message": "^19.8.1", + "@commitlint/to-lines": "^19.8.1", + "@commitlint/types": "^19.8.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.8.1.tgz", + "integrity": "sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.8.1.tgz", + "integrity": "sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@commitlint/top-level/node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/types": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", + "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@cspell/cspell-bundled-dicts": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.1.3.tgz", + "integrity": "sha512-WbOkD32fjxz0hHMP6oTvAgi2VBlzYcqKPNwCo+4b9HefLWV5aiLaxp04d8CeifaAdlYjkjuqRTJXh/HfUeLCVg==", + "dev": true, + "dependencies": { + "@cspell/dict-ada": "^4.1.0", + "@cspell/dict-al": "^1.1.0", + "@cspell/dict-aws": "^4.0.11", + "@cspell/dict-bash": "^4.2.0", + "@cspell/dict-companies": "^3.2.1", + "@cspell/dict-cpp": "^6.0.8", + "@cspell/dict-cryptocurrencies": "^5.0.4", + "@cspell/dict-csharp": "^4.0.6", + "@cspell/dict-css": "^4.0.17", + "@cspell/dict-dart": "^2.3.0", + "@cspell/dict-data-science": "^2.0.8", + "@cspell/dict-django": "^4.1.4", + "@cspell/dict-docker": "^1.1.14", + "@cspell/dict-dotnet": "^5.0.9", + "@cspell/dict-elixir": "^4.0.7", + "@cspell/dict-en_us": "^4.4.13", + "@cspell/dict-en-common-misspellings": "^2.1.2", + "@cspell/dict-en-gb-mit": "^3.1.3", + "@cspell/dict-filetypes": "^3.0.12", + "@cspell/dict-flutter": "^1.1.0", + "@cspell/dict-fonts": "^4.0.4", + "@cspell/dict-fsharp": "^1.1.0", + "@cspell/dict-fullstack": "^3.2.6", + "@cspell/dict-gaming-terms": "^1.1.1", + "@cspell/dict-git": "^3.0.6", + "@cspell/dict-golang": "^6.0.22", + "@cspell/dict-google": "^1.0.8", + "@cspell/dict-haskell": "^4.0.5", + "@cspell/dict-html": "^4.0.11", + "@cspell/dict-html-symbol-entities": "^4.0.3", + "@cspell/dict-java": "^5.0.11", + "@cspell/dict-julia": "^1.1.0", + "@cspell/dict-k8s": "^1.0.11", + "@cspell/dict-kotlin": "^1.1.0", + "@cspell/dict-latex": "^4.0.3", + "@cspell/dict-lorem-ipsum": "^4.0.4", + "@cspell/dict-lua": "^4.0.7", + "@cspell/dict-makefile": "^1.0.4", + "@cspell/dict-markdown": "^2.0.11", + "@cspell/dict-monkeyc": "^1.0.10", + "@cspell/dict-node": "^5.0.7", + "@cspell/dict-npm": "^5.2.9", + "@cspell/dict-php": "^4.0.14", + "@cspell/dict-powershell": "^5.0.14", + "@cspell/dict-public-licenses": "^2.0.13", + "@cspell/dict-python": "^4.2.18", + "@cspell/dict-r": "^2.1.0", + "@cspell/dict-ruby": "^5.0.8", + "@cspell/dict-rust": "^4.0.11", + "@cspell/dict-scala": "^5.0.7", + "@cspell/dict-shell": "^1.1.0", + "@cspell/dict-software-terms": "^5.1.2", + "@cspell/dict-sql": "^2.2.0", + "@cspell/dict-svelte": "^1.0.6", + "@cspell/dict-swift": "^2.0.5", + "@cspell/dict-terraform": "^1.1.2", + "@cspell/dict-typescript": "^3.2.2", + "@cspell/dict-vue": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-json-reporter": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.1.3.tgz", + "integrity": "sha512-FvzlSQuU+bNeo77v0KrA/lkoe324cHvZNhkx7Dtp1aj01FeBr5Y36gozR3DNY6tewBi6hC7uLeeNg/iSBf6CWg==", + "dev": true, + "dependencies": { + "@cspell/cspell-types": "9.1.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-pipe": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.1.3.tgz", + "integrity": "sha512-Cns37ml7IaXMWBci9XOqdTkP9nFtOO8+sJ4VvtbVO68Zo8v0vq74ApDbPgGI2HzYtn7Jj2hxQqGIBdLnmrMPyA==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-resolver": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.1.3.tgz", + "integrity": "sha512-3h9AkbY+YutBG91fQxeSpfIRT50sfrNQ7IAS0N6fCvJ6z0sXed7UPYwf90NauQp/1lN/bVlHFFAgxDEyG720Yg==", + "dev": true, + "dependencies": { + "global-directory": "^4.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-service-bus": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.1.3.tgz", + "integrity": "sha512-Ss4cCnkJI3IHDSOQKxhtAfypvZZDzuJeXbZFVimLvO22/8GdVH+vQxAFm3kBY+ACVUAe13MQIYzZxuFHaM9y8g==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-types": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.1.3.tgz", + "integrity": "sha512-JPLFMp6qKj4fjsEDvMjVXFZg+j3HaRQ7raFtR2RPidYyKcUHPCVhX0wfJ0vuYxkC0Yst+99tgVxR8Wi57xs2Ew==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/dict-ada": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.1.0.tgz", + "integrity": "sha512-7SvmhmX170gyPd+uHXrfmqJBY5qLcCX8kTGURPVeGxmt8XNXT75uu9rnZO+jwrfuU2EimNoArdVy5GZRGljGNg==", + "dev": true + }, + "node_modules/@cspell/dict-al": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.1.0.tgz", + "integrity": "sha512-PtNI1KLmYkELYltbzuoztBxfi11jcE9HXBHCpID2lou/J4VMYKJPNqe4ZjVzSI9NYbMnMnyG3gkbhIdx66VSXg==", + "dev": true + }, + "node_modules/@cspell/dict-aws": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.11.tgz", + "integrity": "sha512-nesbrYbxP/ek7Nc3X1ENWxAXJ/2XIKGxauF0k4VSPLtMvWP50gHAEe+zmqFciFolwIVVjF2l+juDdUdBMPbMiw==", + "dev": true + }, + "node_modules/@cspell/dict-bash": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.0.tgz", + "integrity": "sha512-HOyOS+4AbCArZHs/wMxX/apRkjxg6NDWdt0jF9i9XkvJQUltMwEhyA2TWYjQ0kssBsnof+9amax2lhiZnh3kCg==", + "dev": true, + "dependencies": { + "@cspell/dict-shell": "1.1.0" + } + }, + "node_modules/@cspell/dict-companies": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.1.tgz", + "integrity": "sha512-ryaeJ1KhTTKL4mtinMtKn8wxk6/tqD4vX5tFP+Hg89SiIXmbMk5vZZwVf+eyGUWJOyw5A1CVj9EIWecgoi+jYQ==", + "dev": true + }, + "node_modules/@cspell/dict-cpp": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.8.tgz", + "integrity": "sha512-BzurRZilWqaJt32Gif6/yCCPi+FtrchjmnehVEIFzbWyeBd/VOUw77IwrEzehZsu5cRU91yPWuWp5fUsKfDAXA==", + "dev": true + }, + "node_modules/@cspell/dict-cryptocurrencies": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.4.tgz", + "integrity": "sha512-6iFu7Abu+4Mgqq08YhTKHfH59mpMpGTwdzDB2Y8bbgiwnGFCeoiSkVkgLn1Kel2++hYcZ8vsAW/MJS9oXxuMag==", + "dev": true + }, + "node_modules/@cspell/dict-csharp": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.6.tgz", + "integrity": "sha512-w/+YsqOknjQXmIlWDRmkW+BHBPJZ/XDrfJhZRQnp0wzpPOGml7W0q1iae65P2AFRtTdPKYmvSz7AL5ZRkCnSIw==", + "dev": true + }, + "node_modules/@cspell/dict-css": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.17.tgz", + "integrity": "sha512-2EisRLHk6X/PdicybwlajLGKF5aJf4xnX2uuG5lexuYKt05xV/J/OiBADmi8q9obhxf1nesrMQbqAt+6CsHo/w==", + "dev": true + }, + "node_modules/@cspell/dict-dart": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.0.tgz", + "integrity": "sha512-1aY90lAicek8vYczGPDKr70pQSTQHwMFLbmWKTAI6iavmb1fisJBS1oTmMOKE4ximDf86MvVN6Ucwx3u/8HqLg==", + "dev": true + }, + "node_modules/@cspell/dict-data-science": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.8.tgz", + "integrity": "sha512-uyAtT+32PfM29wRBeAkUSbkytqI8bNszNfAz2sGPtZBRmsZTYugKMEO9eDjAIE/pnT9CmbjNuoiXhk+Ss4fCOg==", + "dev": true + }, + "node_modules/@cspell/dict-django": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.4.tgz", + "integrity": "sha512-fX38eUoPvytZ/2GA+g4bbdUtCMGNFSLbdJJPKX2vbewIQGfgSFJKY56vvcHJKAvw7FopjvgyS/98Ta9WN1gckg==", + "dev": true + }, + "node_modules/@cspell/dict-docker": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.14.tgz", + "integrity": "sha512-p6Qz5mokvcosTpDlgSUREdSbZ10mBL3ndgCdEKMqjCSZJFdfxRdNdjrGER3lQ6LMq5jGr1r7nGXA0gvUJK80nw==", + "dev": true + }, + "node_modules/@cspell/dict-dotnet": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.9.tgz", + "integrity": "sha512-JGD6RJW5sHtO5lfiJl11a5DpPN6eKSz5M1YBa1I76j4dDOIqgZB6rQexlDlK1DH9B06X4GdDQwdBfnpAB0r2uQ==", + "dev": true + }, + "node_modules/@cspell/dict-elixir": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.7.tgz", + "integrity": "sha512-MAUqlMw73mgtSdxvbAvyRlvc3bYnrDqXQrx5K9SwW8F7fRYf9V4vWYFULh+UWwwkqkhX9w03ZqFYRTdkFku6uA==", + "dev": true + }, + "node_modules/@cspell/dict-en_us": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.13.tgz", + "integrity": "sha512-6TEHCJKmRqq7fQI7090p+ju12vhuGcNkc6YfxHrcjO816m53VPVaS6IfG6+6OqelQiOMjr0ZD8IHcDIkwThSFw==", + "dev": true + }, + "node_modules/@cspell/dict-en-common-misspellings": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.2.tgz", + "integrity": "sha512-r74AObInM1XOUxd3lASnNZNDOIA9Bka7mBDTkvkOeCGoLQhn+Cr7h1889u4K07KHbecKMHP6zw5zQhkdocNzCw==", + "dev": true + }, + "node_modules/@cspell/dict-en-gb-mit": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.3.tgz", + "integrity": "sha512-4aY8ySQxSNSRILtf9lJIfSR+su86u8VL6z41gOIhvLIvYnHMFiohV7ebM91GbtdZXBazL7zmGFcpm2EnBzewug==", + "dev": true + }, + "node_modules/@cspell/dict-filetypes": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.12.tgz", + "integrity": "sha512-+ds5wgNdlUxuJvhg8A1TjuSpalDFGCh7SkANCWvIplg6QZPXL4j83lqxP7PgjHpx7PsBUS7vw0aiHPjZy9BItw==", + "dev": true + }, + "node_modules/@cspell/dict-flutter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-flutter/-/dict-flutter-1.1.0.tgz", + "integrity": "sha512-3zDeS7zc2p8tr9YH9tfbOEYfopKY/srNsAa+kE3rfBTtQERAZeOhe5yxrnTPoufctXLyuUtcGMUTpxr3dO0iaA==", + "dev": true + }, + "node_modules/@cspell/dict-fonts": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.4.tgz", + "integrity": "sha512-cHFho4hjojBcHl6qxidl9CvUb492IuSk7xIf2G2wJzcHwGaCFa2o3gRcxmIg1j62guetAeDDFELizDaJlVRIOg==", + "dev": true + }, + "node_modules/@cspell/dict-fsharp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.1.0.tgz", + "integrity": "sha512-oguWmHhGzgbgbEIBKtgKPrFSVAFtvGHaQS0oj+vacZqMObwkapcTGu7iwf4V3Bc2T3caf0QE6f6rQfIJFIAVsw==", + "dev": true + }, + "node_modules/@cspell/dict-fullstack": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.6.tgz", + "integrity": "sha512-cSaq9rz5RIU9j+0jcF2vnKPTQjxGXclntmoNp4XB7yFX2621PxJcekGjwf/lN5heJwVxGLL9toR0CBlGKwQBgA==", + "dev": true + }, + "node_modules/@cspell/dict-gaming-terms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.1.1.tgz", + "integrity": "sha512-tb8GFxjTLDQstkJcJ90lDqF4rKKlMUKs5/ewePN9P+PYRSehqDpLI5S5meOfPit8LGszeOrjUdBQ4zXo7NpMyQ==", + "dev": true + }, + "node_modules/@cspell/dict-git": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.6.tgz", + "integrity": "sha512-nazfOqyxlBOQGgcur9ssEOEQCEZkH8vXfQe8SDEx8sCN/g0SFm8ktabgLVmBOXjy3RzjVNLlM2nBfRQ7e6+5hQ==", + "dev": true + }, + "node_modules/@cspell/dict-golang": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.22.tgz", + "integrity": "sha512-FvV0m3Y0nUFxw36uDCD8UtfOPv4wsZnnlabNwB3xNZ2IBn0gBURuMUZywScb9sd2wXM8VFBRoU//tc6NQsOVOg==", + "dev": true + }, + "node_modules/@cspell/dict-google": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-google/-/dict-google-1.0.8.tgz", + "integrity": "sha512-BnMHgcEeaLyloPmBs8phCqprI+4r2Jb8rni011A8hE+7FNk7FmLE3kiwxLFrcZnnb7eqM0agW4zUaNoB0P+z8A==", + "dev": true + }, + "node_modules/@cspell/dict-haskell": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.5.tgz", + "integrity": "sha512-s4BG/4tlj2pPM9Ha7IZYMhUujXDnI0Eq1+38UTTCpatYLbQqDwRFf2KNPLRqkroU+a44yTUAe0rkkKbwy4yRtQ==", + "dev": true + }, + "node_modules/@cspell/dict-html": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.11.tgz", + "integrity": "sha512-QR3b/PB972SRQ2xICR1Nw/M44IJ6rjypwzA4jn+GH8ydjAX9acFNfc+hLZVyNe0FqsE90Gw3evLCOIF0vy1vQw==", + "dev": true + }, + "node_modules/@cspell/dict-html-symbol-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.3.tgz", + "integrity": "sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A==", + "dev": true + }, + "node_modules/@cspell/dict-java": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.11.tgz", + "integrity": "sha512-T4t/1JqeH33Raa/QK/eQe26FE17eUCtWu+JsYcTLkQTci2dk1DfcIKo8YVHvZXBnuM43ATns9Xs0s+AlqDeH7w==", + "dev": true + }, + "node_modules/@cspell/dict-julia": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-julia/-/dict-julia-1.1.0.tgz", + "integrity": "sha512-CPUiesiXwy3HRoBR3joUseTZ9giFPCydSKu2rkh6I2nVjXnl5vFHzOMLXpbF4HQ1tH2CNfnDbUndxD+I+7eL9w==", + "dev": true + }, + "node_modules/@cspell/dict-k8s": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.11.tgz", + "integrity": "sha512-8ojNwB5j4PfZ1Gq9n5c/HKJCtZD3h6+wFy+zpALpDWFFQ2qT22Be30+3PVd+G5gng8or0LeK8VgKKd0l1uKPTA==", + "dev": true + }, + "node_modules/@cspell/dict-kotlin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-kotlin/-/dict-kotlin-1.1.0.tgz", + "integrity": "sha512-vySaVw6atY7LdwvstQowSbdxjXG6jDhjkWVWSjg1XsUckyzH1JRHXe9VahZz1i7dpoFEUOWQrhIe5B9482UyJQ==", + "dev": true + }, + "node_modules/@cspell/dict-latex": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.3.tgz", + "integrity": "sha512-2KXBt9fSpymYHxHfvhUpjUFyzrmN4c4P8mwIzweLyvqntBT3k0YGZJSriOdjfUjwSygrfEwiuPI1EMrvgrOMJw==", + "dev": true + }, + "node_modules/@cspell/dict-lorem-ipsum": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.4.tgz", + "integrity": "sha512-+4f7vtY4dp2b9N5fn0za/UR0kwFq2zDtA62JCbWHbpjvO9wukkbl4rZg4YudHbBgkl73HRnXFgCiwNhdIA1JPw==", + "dev": true + }, + "node_modules/@cspell/dict-lua": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.7.tgz", + "integrity": "sha512-Wbr7YSQw+cLHhTYTKV6cAljgMgcY+EUAxVIZW3ljKswEe4OLxnVJ7lPqZF5JKjlXdgCjbPSimsHqyAbC5pQN/Q==", + "dev": true + }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.4.tgz", + "integrity": "sha512-E4hG/c0ekPqUBvlkrVvzSoAA+SsDA9bLi4xSV3AXHTVru7Y2bVVGMPtpfF+fI3zTkww/jwinprcU1LSohI3ylw==", + "dev": true + }, + "node_modules/@cspell/dict-markdown": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.11.tgz", + "integrity": "sha512-stZieFKJyMQbzKTVoalSx2QqCpB0j8nPJF/5x+sBnDIWgMC65jp8Wil+jccWh9/vnUVukP3Ejewven5NC7SWuQ==", + "dev": true, + "peerDependencies": { + "@cspell/dict-css": "^4.0.17", + "@cspell/dict-html": "^4.0.11", + "@cspell/dict-html-symbol-entities": "^4.0.3", + "@cspell/dict-typescript": "^3.2.2" + } + }, + "node_modules/@cspell/dict-monkeyc": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.10.tgz", + "integrity": "sha512-7RTGyKsTIIVqzbvOtAu6Z/lwwxjGRtY5RkKPlXKHEoEAgIXwfDxb5EkVwzGQwQr8hF/D3HrdYbRT8MFBfsueZw==", + "dev": true + }, + "node_modules/@cspell/dict-node": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.7.tgz", + "integrity": "sha512-ZaPpBsHGQCqUyFPKLyCNUH2qzolDRm1/901IO8e7btk7bEDF56DN82VD43gPvD4HWz3yLs/WkcLa01KYAJpnOw==", + "dev": true + }, + "node_modules/@cspell/dict-npm": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.10.tgz", + "integrity": "sha512-MGR5S5e/0zcX3ln4eXQNYs3HBkX/JciqAmnCS0mNVx2jic1TtWBxJx+a33+95OhZeWF2Z/qL63FUQqZrJL27VA==", + "dev": true + }, + "node_modules/@cspell/dict-php": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.14.tgz", + "integrity": "sha512-7zur8pyncYZglxNmqsRycOZ6inpDoVd4yFfz1pQRe5xaRWMiK3Km4n0/X/1YMWhh3e3Sl/fQg5Axb2hlN68t1g==", + "dev": true + }, + "node_modules/@cspell/dict-powershell": { + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.14.tgz", + "integrity": "sha512-ktjjvtkIUIYmj/SoGBYbr3/+CsRGNXGpvVANrY0wlm/IoGlGywhoTUDYN0IsGwI2b8Vktx3DZmQkfb3Wo38jBA==", + "dev": true + }, + "node_modules/@cspell/dict-public-licenses": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.13.tgz", + "integrity": "sha512-1Wdp/XH1ieim7CadXYE7YLnUlW0pULEjVl9WEeziZw3EKCAw8ZI8Ih44m4bEa5VNBLnuP5TfqC4iDautAleQzQ==", + "dev": true + }, + "node_modules/@cspell/dict-python": { + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.18.tgz", + "integrity": "sha512-hYczHVqZBsck7DzO5LumBLJM119a3F17aj8a7lApnPIS7cmEwnPc2eACNscAHDk7qAo2127oI7axUoFMe9/g1g==", + "dev": true, + "dependencies": { + "@cspell/dict-data-science": "^2.0.8" + } + }, + "node_modules/@cspell/dict-r": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.1.0.tgz", + "integrity": "sha512-k2512wgGG0lTpTYH9w5Wwco+lAMf3Vz7mhqV8+OnalIE7muA0RSuD9tWBjiqLcX8zPvEJr4LdgxVju8Gk3OKyA==", + "dev": true + }, + "node_modules/@cspell/dict-ruby": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.8.tgz", + "integrity": "sha512-ixuTneU0aH1cPQRbWJvtvOntMFfeQR2KxT8LuAv5jBKqQWIHSxzGlp+zX3SVyoeR0kOWiu64/O5Yn836A5yMcQ==", + "dev": true + }, + "node_modules/@cspell/dict-rust": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.11.tgz", + "integrity": "sha512-OGWDEEzm8HlkSmtD8fV3pEcO2XBpzG2XYjgMCJCRwb2gRKvR+XIm6Dlhs04N/K2kU+iH8bvrqNpM8fS/BFl0uw==", + "dev": true + }, + "node_modules/@cspell/dict-scala": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.7.tgz", + "integrity": "sha512-yatpSDW/GwulzO3t7hB5peoWwzo+Y3qTc0pO24Jf6f88jsEeKmDeKkfgPbYuCgbE4jisGR4vs4+jfQZDIYmXPA==", + "dev": true + }, + "node_modules/@cspell/dict-shell": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.1.0.tgz", + "integrity": "sha512-D/xHXX7T37BJxNRf5JJHsvziFDvh23IF/KvkZXNSh8VqcRdod3BAz9VGHZf6VDqcZXr1VRqIYR3mQ8DSvs3AVQ==", + "dev": true + }, + "node_modules/@cspell/dict-software-terms": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.3.tgz", + "integrity": "sha512-kHQmiMvAuXvF54S1uLZNVUJatnDv8L+pRnPMAiFXPTdudi6oM04TzI8yj8anm7gLcpfmJLCIECnc3s+we5eeXw==", + "dev": true + }, + "node_modules/@cspell/dict-sql": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.2.0.tgz", + "integrity": "sha512-MUop+d1AHSzXpBvQgQkCiok8Ejzb+nrzyG16E8TvKL2MQeDwnIvMe3bv90eukP6E1HWb+V/MA/4pnq0pcJWKqQ==", + "dev": true + }, + "node_modules/@cspell/dict-svelte": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.6.tgz", + "integrity": "sha512-8LAJHSBdwHCoKCSy72PXXzz7ulGROD0rP1CQ0StOqXOOlTUeSFaJJlxNYjlONgd2c62XBQiN2wgLhtPN+1Zv7Q==", + "dev": true + }, + "node_modules/@cspell/dict-swift": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.5.tgz", + "integrity": "sha512-3lGzDCwUmnrfckv3Q4eVSW3sK3cHqqHlPprFJZD4nAqt23ot7fic5ALR7J4joHpvDz36nHX34TgcbZNNZOC/JA==", + "dev": true + }, + "node_modules/@cspell/dict-terraform": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.1.2.tgz", + "integrity": "sha512-RB9dnhxKIiWpwQB+b3JuFa8X4m+6Ny92Y4Z5QARR7jEtapg8iF2ODZX1yLtozp4kFVoRsUKEP6vj3MLv87VTdg==", + "dev": true + }, + "node_modules/@cspell/dict-typescript": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.2.tgz", + "integrity": "sha512-H9Y+uUHsTIDFO/jdfUAcqmcd5osT+2DB5b0aRCHfLWN/twUbGn/1qq3b7YwEvttxKlYzWHU3uNFf+KfA93VY7w==", + "dev": true + }, + "node_modules/@cspell/dict-vue": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.4.tgz", + "integrity": "sha512-0dPtI0lwHcAgSiQFx8CzvqjdoXROcH+1LyqgROCpBgppommWpVhbQ0eubnKotFEXgpUCONVkeZJ6Ql8NbTEu+w==", + "dev": true + }, + "node_modules/@cspell/dynamic-import": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.1.3.tgz", + "integrity": "sha512-+8PxTslsh+oTxmhYdnfQZ/brYGFAnfqLR9xotWE4Ks3HoaLOhZsp6FF9kvlEp/gNOjpyhHn1UhT/Gr5fT4+QhQ==", + "dev": true, + "dependencies": { + "@cspell/url": "9.1.3", + "import-meta-resolve": "^4.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/filetypes": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.1.3.tgz", + "integrity": "sha512-HRJEggDo6OJJmCc/gq7oriMqkqVDema+oLpGBh1a/M7ulw+CzoHkOa//1ohpAJh5KsWj9Tej9Va4BUZ/SaCwUA==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/strong-weak-map": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.1.3.tgz", + "integrity": "sha512-+96SI9R6TOY+xGBOK5LiOgX/W/9gAKus1Cvngh2LdtDVZwgVqpqvm5LoXxLhUT+Vs5UsndRBzblSdNpziSwZtA==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/url": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.1.3.tgz", + "integrity": "sha512-LQQKY0O4QYUNKyDod8VfEBvqeJNGHJlx1v0gDq00eMvaClnkIz+y2ObGdtDlF7ZbG7TgI6PQ3ahJdlqfRPe3ZQ==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@datocms/cma-client": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@datocms/cma-client/-/cma-client-4.0.2.tgz", + "integrity": "sha512-AhtI7hGZxjHcScE+Wkk01BFgkiL+tNft0oMDyuKlVEKF0zRCqRHipwf8lwfO5zQP4XFQ6Qn4Fh/B8oF30q/BIA==", + "dependencies": { + "@datocms/rest-client-utils": "^4.0.2", + "uuid": "^9.0.1" + } + }, + "node_modules/@datocms/cma-client-node": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@datocms/cma-client-node/-/cma-client-node-4.0.2.tgz", + "integrity": "sha512-tiduimUbtH28Uc5gcBZu5QriMXdjb1vnw60hp6n5CKicVDQTm7703W3yp0fVWyDCW4gKLS66rfKzAobPn9zgyA==", + "dependencies": { + "@datocms/cma-client": "^4.0.2", + "@datocms/rest-client-utils": "^4.0.2", + "mime-types": "^2.1.35", + "tmp-promise": "^3.0.3" + } + }, + "node_modules/@datocms/rest-client-utils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@datocms/rest-client-utils/-/rest-client-utils-4.0.2.tgz", + "integrity": "sha512-aKg8bl9mHebWSnBgX1f3Y9hGkX0b+fHlEmhTvZ2JqzrVcu7RZjEWe612RFpyuTISv3hgIRbgmcE7KDfJe/WnfA==", + "dependencies": { + "async-scheduler": "^1.4.4" + } + }, + "node_modules/@emnapi/core": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", + "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", + "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", + "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.9.tgz", + "integrity": "sha512-DBJBkzI5Wx4jFaYm221LHvAhpKYkhVS0k9plqHwaHhofGNxvYB7J3Bz8w+bFJ05zaMb0sZNHo4KdmENQFlNTuQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.13.tgz", + "integrity": "sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.14.tgz", + "integrity": "sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.14.tgz", + "integrity": "sha512-yd2qtLl4QIIax9DTMZ1ZN2pFrrj+yL3kgIWxm34SS6uwCr0sIhsNyudUjAo5q3TqI03xx4SEBkUJqZuAInp9uA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.16.tgz", + "integrity": "sha512-oiDqafWzMtofeJyyGkb1CTPaxUkjIcSxePHHQCfif8t3HV9pHcw1Kgdw3/uGpDvaFfeTluwQtWiqzPVjAqS3zA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", + "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.0.tgz", + "integrity": "sha512-opqpHPB1NjAmDISi3uvZOTrjEEU5CWVu/HBkDby8t93+6UxYX0Z7Ps0Ltjm5sZiEbWenjubwUkivAEYQmy9xHw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.16.tgz", + "integrity": "sha512-kMrXAaKGavBEoBYUCgualbwA9jWUx2TjMA46ek+pEKy38+LFpL9QHlTd8PO2kWPUgI/KB+qi02o4y2rwXbzr3Q==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.16.tgz", + "integrity": "sha512-g8BVNBj5Zeb5/Y3cSN+hDUL7CsIFDIuVxb9EPty3lkxBaYpjL5BNRKSYOF9yOLe+JOcKFd+TSVeADQ4iSY7rbg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.6.0.tgz", + "integrity": "sha512-jAhL7tyMxB3Gfwn4HIJ0yuJ5pvcB5maYUcouGcgd/ub79f9MqZ+aVnBtuFf+VC2GTkCBF+R+eo7Vi63w5VZlzw==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.9", + "@inquirer/confirm": "^5.1.13", + "@inquirer/editor": "^4.2.14", + "@inquirer/expand": "^4.0.16", + "@inquirer/input": "^4.2.0", + "@inquirer/number": "^3.0.16", + "@inquirer/password": "^4.0.16", + "@inquirer/rawlist": "^4.1.4", + "@inquirer/search": "^3.0.16", + "@inquirer/select": "^4.2.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.4.tgz", + "integrity": "sha512-5GGvxVpXXMmfZNtvWw4IsHpR7RzqAR624xtkPd1NxxlV5M+pShMqzL4oRddRkg8rVEOK9fKdJp1jjVML2Lr7TQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.16.tgz", + "integrity": "sha512-POCmXo+j97kTGU6aeRjsPyuCpQQfKcMXdeTMw708ZMtWrj5aykZvlUxH4Qgz3+Y1L/cAVZsSpA+UgZCu2GMOMg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.4.tgz", + "integrity": "sha512-unTppUcTjmnbl/q+h8XeQDhAqIOmwWYWNyiiP2e3orXrg6tOaa5DHXja9PChCSbChOsktyKgOieRZFnajzxoBg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", + "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.4.tgz", + "integrity": "sha512-tMLCDvBJBwPqMm4OAiuKm2uF5y5Qe26KgcMn+nrDSWpEW+eeFmqA0iO4zJfL16GP7gE3bUUQ3hIuUJ22AqVRnw==", + "dev": true, + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.0.2", + "jest-util": "30.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.4.tgz", + "integrity": "sha512-MWScSO9GuU5/HoWjpXAOBs6F/iobvK1XlioelgOM9St7S0Z5WTI9kjCQLPeo4eQRRYusyLW25/J7J5lbFkrYXw==", + "dev": true, + "dependencies": { + "@jest/console": "30.0.4", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.0.4", + "@jest/test-result": "30.0.4", + "@jest/transform": "30.0.4", + "@jest/types": "30.0.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.0.2", + "jest-config": "30.0.4", + "jest-haste-map": "30.0.2", + "jest-message-util": "30.0.2", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.2", + "jest-resolve-dependencies": "30.0.4", + "jest-runner": "30.0.4", + "jest-runtime": "30.0.4", + "jest-snapshot": "30.0.4", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "jest-watcher": "30.0.4", + "micromatch": "^4.0.8", + "pretty-format": "30.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.4.tgz", + "integrity": "sha512-5NT+sr7ZOb8wW7C4r7wOKnRQ8zmRWQT2gW4j73IXAKp5/PX1Z8MCStBLQDYfIG3n1Sw0NRfYGdp0iIPVooBAFQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "30.0.4", + "@jest/types": "30.0.1", + "@types/node": "*", + "jest-mock": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.4.tgz", + "integrity": "sha512-Z/DL7t67LBHSX4UzDyeYKqOxE/n7lbrrgEwWM3dGiH5Dgn35nk+YtgzKudmfIrBI8DRRrKYY5BCo3317HZV1Fw==", + "dev": true, + "dependencies": { + "expect": "30.0.4", + "jest-snapshot": "30.0.4" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.4.tgz", + "integrity": "sha512-EgXecHDNfANeqOkcak0DxsoVI4qkDUsR7n/Lr2vtmTBjwLPBnnPOF71S11Q8IObWzxm2QgQoY6f9hzrRD3gHRA==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.4.tgz", + "integrity": "sha512-qZ7nxOcL5+gwBO6LErvwVy5k06VsX/deqo2XnVUSTV0TNC9lrg8FC3dARbi+5lmrr5VyX5drragK+xLcOjvjYw==", + "dev": true, + "dependencies": { + "@jest/types": "30.0.1", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.0.2", + "jest-mock": "30.0.2", + "jest-util": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", + "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.4.tgz", + "integrity": "sha512-avyZuxEHF2EUhFF6NEWVdxkRRV6iXXcIES66DLhuLlU7lXhtFG/ySq/a8SRZmEJSsLkNAFX6z6mm8KWyXe9OEA==", + "dev": true, + "dependencies": { + "@jest/environment": "30.0.4", + "@jest/expect": "30.0.4", + "@jest/types": "30.0.1", + "jest-mock": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.4.tgz", + "integrity": "sha512-6ycNmP0JSJEEys1FbIzHtjl9BP0tOZ/KN6iMeAKrdvGmUsa1qfRdlQRUDKJ4P84hJ3xHw1yTqJt4fvPNHhyE+g==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.0.4", + "@jest/test-result": "30.0.4", + "@jest/transform": "30.0.4", + "@jest/types": "30.0.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.0.2", + "jest-util": "30.0.2", + "jest-worker": "30.0.2", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.1.tgz", + "integrity": "sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.4.tgz", + "integrity": "sha512-BEpX8M/Y5lG7MI3fmiO+xCnacOrVsnbqVrcDZIT8aSGkKV1w2WwvRQxSWw5SIS8ozg7+h8tSj5EO1Riqqxcdag==", + "dev": true, + "dependencies": { + "@jest/types": "30.0.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/test-result": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.4.tgz", + "integrity": "sha512-Mfpv8kjyKTHqsuu9YugB6z1gcdB3TSSOaKlehtVaiNlClMkEHY+5ZqCY2CrEE3ntpBMlstX/ShDAf84HKWsyIw==", + "dev": true, + "dependencies": { + "@jest/console": "30.0.4", + "@jest/types": "30.0.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.4.tgz", + "integrity": "sha512-bj6ePmqi4uxAE8EHE0Slmk5uBYd9Vd/PcVt06CsBxzH4bbA8nGsI1YbXl/NH+eii4XRtyrRx+Cikub0x8H4vDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.0.4", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.4.tgz", + "integrity": "sha512-atvy4hRph/UxdCIBp+UB2jhEA/jJiUeGZ7QPgBi9jUUKNgi3WEoMXGNG7zbbELG2+88PMabUNCDchmqgJy3ELg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.0.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.0", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.2", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/types": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.1.tgz", + "integrity": "sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==", + "dev": true, + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", + "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", + "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.2.2", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", + "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-12.0.0.tgz", + "integrity": "sha512-MPd6WK1VtZ52lFrgZ0R2FlaoiWllzgqFHaSZxvp72NmoDeZ0m8GeJdg4oB6ctqMTYyrnDYp592Xma21mrgiyDA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz", + "integrity": "sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-10.0.0.tgz", + "integrity": "sha512-Kuq5/qs0DVYTHZuBAzCZStCzo2nKvVRo/TDNhCcpC2TKiOGz/DisXMCvjt3/b5kr6SCI1Y8eeeJTHBxxpFvZEg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^6.1.3" + } + }, + "node_modules/@octokit/request": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", + "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", + "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/openapi-types": "^25.0.0" + } + }, + "node_modules/@oxlint/darwin-arm64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.6.0.tgz", + "integrity": "sha512-m3wyqBh1TOHjpr/dXeIZY7OoX+MQazb+bMHQdDtwUvefrafUx+5YHRvulYh1sZSQ449nQ3nk3qj5qj535vZRjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/darwin-x64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.6.0.tgz", + "integrity": "sha512-75fJfF/9xNypr7cnOYoZBhfmG1yP7ex3pUOeYGakmtZRffO9z1i1quLYhjZsmaDXsAIZ3drMhenYHMmFKS3SRg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/linux-arm64-gnu": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.6.0.tgz", + "integrity": "sha512-YhXGf0FXa72bEt4F7eTVKx5X3zWpbAOPnaA/dZ6/g8tGhw1m9IFjrabVHFjzcx3dQny4MgA59EhyElkDvpUe8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-arm64-musl": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.6.0.tgz", + "integrity": "sha512-T3JDhx8mjGjvh5INsPZJrlKHmZsecgDYvtvussKRdkc1Nnn7WC+jH9sh5qlmYvwzvmetlPVNezAoNvmGO9vtMg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-gnu": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.6.0.tgz", + "integrity": "sha512-Dx7ghtAl8aXBdqofJpi338At6lkeCtTfoinTYQXd9/TEJx+f+zCGNlQO6nJz3ydJBX48FDuOFKkNC+lUlWrd8w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-musl": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.6.0.tgz", + "integrity": "sha512-7KvMGdWmAZtAtg6IjoEJHKxTXdAcrHnUnqfgs0JpXst7trquV2mxBeRZusQXwxpu4HCSomKMvJfsp1qKaqSFDg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/win32-arm64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.6.0.tgz", + "integrity": "sha512-iSGC9RwX+dl7o5KFr5aH7Gq3nFbkq/3Gda6mxNPMvNkWrgXdIyiINxpyD8hJu566M+QSv1wEAu934BZotFDyoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/win32-x64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.6.0.tgz", + "integrity": "sha512-jOj3L/gfLc0IwgOTkZMiZ5c673i/hbAmidlaylT0gE6H18hln9HxPgp5GCf4E4y6mwEJlW8QC5hQi221+9otdA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@semantic-release/changelog": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", + "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^11.0.0", + "lodash": "^4.17.4" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz", + "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-changelog-angular": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.1.0.tgz", + "integrity": "sha512-5nxDo7TwKB5InYBl4ZC//1g9GRwB/F3TXOGR9hgUjMGfvSP4Vu5NkpNro2+1+TIEy1vwxApl5ircECr2ri5JIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/commit-analyzer/node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/git": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", + "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/github": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.2.tgz", + "integrity": "sha512-EhHimj3/eOSPu0OflgDzwgrawoGJIn8XLOkNS6WzwuTr8ebxyX976Y4mCqJ8MlkdQpV5+8T+49sy8xXlcm6uCg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/core": "^6.0.0", + "@octokit/plugin-paginate-rest": "^12.0.0", + "@octokit/plugin-retry": "^7.0.0", + "@octokit/plugin-throttling": "^10.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=24.1.0" + } + }, + "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/github/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/github/node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/github/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/github/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.2.tgz", + "integrity": "sha512-+M9/Lb35IgnlUO6OSJ40Ie+hUsZLuph2fqXC/qrKn0fMvUU/jiCjpoL6zEm69vzcmaZJ8yNKtMBEKHWN49WBbQ==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^9.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.9.3", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/npm/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/execa": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", + "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@semantic-release/npm/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz", + "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^2.0.0", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-package-up": "^11.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.1.0.tgz", + "integrity": "sha512-5nxDo7TwKB5InYBl4ZC//1g9GRwB/F3TXOGR9hgUjMGfvSP4Vu5NkpNro2+1+TIEy1vwxApl5ircECr2ri5JIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.37", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.37.tgz", + "integrity": "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==", + "dev": true + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", + "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/inquirer": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.8.tgz", + "integrity": "sha512-CgPD5kFGWsb8HJ5K7rfWlifao87m4ph8uioU7OTncJevmE/VLIqAAjfQtko578JZg7/f69K4FgqYym3gNr7DeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz", + "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", + "devOptional": true, + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/pluralize": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.33.tgz", + "integrity": "sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/through": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.0.tgz", + "integrity": "sha512-LRw5BW29sYj9NsQC6QoqeLVQhEa+BwVINYyMlcve+6stwdBsSt5UB7zw4UZB4+4PNqIVilHoMaPWCb/KhABHQw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.0.tgz", + "integrity": "sha512-zYX8D2zcWCAHqghA8tPjbp7LwjVXbIZP++mpU/Mrf5jUVlk3BWIxkeB8yYzZi5GpFSlqMcRZQxQqbMI0c2lASQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.0.tgz", + "integrity": "sha512-YsYOT049hevAY/lTYD77GhRs885EXPeAfExG5KenqMJ417nYLS2N/kpRpYbABhFZBVQn+2uRPasTe4ypmYoo3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.0.tgz", + "integrity": "sha512-PSjvk3OZf1aZImdGY5xj9ClFG3bC4gnSSYWrt+id0UAv+GwwVldhpMFjAga8SpMo2T1GjV9UKwM+QCsQCQmtdA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.0.tgz", + "integrity": "sha512-KC/iFaEN/wsTVYnHClyHh5RSYA9PpuGfqkFua45r4sweXpC0KHZ+BYY7ikfcGPt5w1lMpR1gneFzuqWLQxsRKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.0.tgz", + "integrity": "sha512-CDh/0v8uot43cB4yKtDL9CVY8pbPnMV0dHyQCE4lFz6PW/+9tS0i9eqP5a91PAqEBVMqH1ycu+k8rP6wQU846w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.0.tgz", + "integrity": "sha512-+TE7epATDSnvwr3L/hNHX3wQ8KQYB+jSDTdywycg3qDqvavRP8/HX9qdq/rMcnaRDn4EOtallb3vL/5wCWGCkw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.0.tgz", + "integrity": "sha512-VBAYGg3VahofpQ+L4k/ZO8TSICIbUKKTaMYOWHWfuYBFqPbSkArZZLezw3xd27fQkxX4BaLGb/RKnW0dH9Y/UA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.0.tgz", + "integrity": "sha512-9IgGFUUb02J1hqdRAHXpZHIeUHRrbnGo6vrRbz0fREH7g+rzQy53/IBSyadZ/LG5iqMxukriNPu4hEMUn+uWEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.0.tgz", + "integrity": "sha512-LR4iQ/LPjMfivpL2bQ9kmm3UnTas3U+umcCnq/CV7HAkukVdHxrDD1wwx74MIWbbgzQTLPYY7Ur2MnnvkYJCBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.0.tgz", + "integrity": "sha512-HCupFQwMrRhrOg7YHrobbB5ADg0Q8RNiuefqMHVsdhEy9lLyXm/CxsCXeLJdrg27NAPsCaMDtdlm8Z2X8x91Tg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.0.tgz", + "integrity": "sha512-Ckxy76A5xgjWa4FNrzcKul5qFMWgP5JSQ5YKd0XakmWOddPLSkQT+uAvUpQNnFGNbgKzv90DyQlxPDYPQ4nd6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.0.tgz", + "integrity": "sha512-HfO0PUCCRte2pMJmVyxPI+eqT7KuV3Fnvn2RPvMe5mOzb2BJKf4/Vth8sSt9cerQboMaTVpbxyYjjLBWIuI5BQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.0.tgz", + "integrity": "sha512-9PZdjP7tLOEjpXHS6+B/RNqtfVUyDEmaViPOuSqcbomLdkJnalt5RKQ1tr2m16+qAufV0aDkfhXtoO7DQos/jg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.0.tgz", + "integrity": "sha512-qkE99ieiSKMnFJY/EfyGKVtNra52/k+lVF/PbO4EL5nU6AdvG4XhtJ+WHojAJP7ID9BNIra/yd75EHndewNRfA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.0.tgz", + "integrity": "sha512-MjXek8UL9tIX34gymvQLecz2hMaQzOlaqYJJBomwm1gsvK2F7hF+YqJJ2tRyBDTv9EZJGMt4KlKkSD/gZWCOiw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.0.tgz", + "integrity": "sha512-9LT6zIGO7CHybiQSh7DnQGwFMZvVr0kUjah6qQfkH2ghucxPV6e71sUXJdSM4Ba0MaGE6DC/NwWf7mJmc3DAng==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.0.tgz", + "integrity": "sha512-HYchBYOZ7WN266VjoGm20xFv5EonG/ODURRgwl9EZT7Bq1nLEs6VKJddzfFdXEAho0wfFlt8L/xIiE29Pmy1RA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.0.tgz", + "integrity": "sha512-+oLKLHw3I1UQo4MeHfoLYF+e6YBa8p5vYUw3Rgt7IDzCs+57vIZqQlIo62NDpYM0VG6BjWOwnzBczMvbtH8hag==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-scheduler": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/async-scheduler/-/async-scheduler-1.4.4.tgz", + "integrity": "sha512-KVWlF6WKlUGJA8FOJYVVk7xDm3PxITWXp9hTeDsXMtUPvItQ2x6g2rIeNAa0TtAiiWvHJqhyA3wo+pj0rA7Ldg==" + }, + "node_modules/babel-jest": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.4.tgz", + "integrity": "sha512-UjG2j7sAOqsp2Xua1mS/e+ekddkSu3wpf4nZUSvXNHuVWdaOUXQ77+uyjJLDE9i0atm5x4kds8K9yb5lRsRtcA==", + "dev": true, + "dependencies": { + "@jest/transform": "30.0.4", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.0", + "babel-preset-jest": "30.0.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", + "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", + "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", + "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "30.0.1", + "babel-preset-current-node-syntax": "^1.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz", + "integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", + "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/clear-module": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", + "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", + "dev": true, + "dependencies": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cli-highlight/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", + "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/comment-json": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", + "dev": true, + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.1.tgz", + "integrity": "sha512-hlqcy3xHred2gyYg/zXSMXraY2mjAYYo0msUCpK+BGyaVJMFCKWVXPIHiaacGO2GGp13kvHWXFhYmxT4QQqW3Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-writer/node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cspell": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.1.3.tgz", + "integrity": "sha512-QxpQn9rGIZN/neMU4hx9T4s9AL5nyRhumNCdYHjjU8Pi4ztZOzuVWbOQD1Oq5ygb92Aci76/DwbJQ1dmb4631Q==", + "dev": true, + "dependencies": { + "@cspell/cspell-json-reporter": "9.1.3", + "@cspell/cspell-pipe": "9.1.3", + "@cspell/cspell-types": "9.1.3", + "@cspell/dynamic-import": "9.1.3", + "@cspell/url": "9.1.3", + "chalk": "^5.4.1", + "chalk-template": "^1.1.0", + "commander": "^14.0.0", + "cspell-config-lib": "9.1.3", + "cspell-dictionary": "9.1.3", + "cspell-gitignore": "9.1.3", + "cspell-glob": "9.1.3", + "cspell-io": "9.1.3", + "cspell-lib": "9.1.3", + "fast-json-stable-stringify": "^2.1.0", + "file-entry-cache": "^9.1.0", + "semver": "^7.7.2", + "tinyglobby": "^0.2.14" + }, + "bin": { + "cspell": "bin.mjs", + "cspell-esm": "bin.mjs" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" + } + }, + "node_modules/cspell-config-lib": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.1.3.tgz", + "integrity": "sha512-B3DdOTZNIOQahSkOYqaq2fOc8fq/jFkrOFd36kge/GAyEpY2Um/Kp/GQ6caOcev+ju0h3iGaO24OLCx6QJ3YoQ==", + "dev": true, + "dependencies": { + "@cspell/cspell-types": "9.1.3", + "comment-json": "^4.2.5", + "smol-toml": "^1.4.1", + "yaml": "^2.8.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-dictionary": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.1.3.tgz", + "integrity": "sha512-BXWwYQ64LaSOd7+8TLZax3AeUnTJUuIl+Tl32/dqcVpgDF4P0eAUVE5xap+QZ2rzKRVFjD8r5M6IR2QrA23o0g==", + "dev": true, + "dependencies": { + "@cspell/cspell-pipe": "9.1.3", + "@cspell/cspell-types": "9.1.3", + "cspell-trie-lib": "9.1.3", + "fast-equals": "^5.2.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-gitignore": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.1.3.tgz", + "integrity": "sha512-yc7Td6L7ZHejm1OzwY/hyfBgyz3gpToMPDyztwbwOdrxXNLRIgDZVPvjVS67XvNf3dv55J19A/8r5Xd7yaV60w==", + "dev": true, + "dependencies": { + "@cspell/url": "9.1.3", + "cspell-glob": "9.1.3", + "cspell-io": "9.1.3" + }, + "bin": { + "cspell-gitignore": "bin.mjs" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-glob": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.1.3.tgz", + "integrity": "sha512-If7gSgbWlUhLcmNA9zPflWzdUZs4wyRKB/Ze584wrht7zJR4yJm2Rptk2+M8kXEhx3zYS6UGhSL0alPbVAbjgQ==", + "dev": true, + "dependencies": { + "@cspell/url": "9.1.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-glob/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/cspell-grammar": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.1.3.tgz", + "integrity": "sha512-L1OVY9RyZXPT+qesw0c7aRKTxQIC7nrLKDQ97hRrQhK23hv5Q8o7GVs1S7pXRNZ/oA8V+VNG2CgjLiKnVM2jnw==", + "dev": true, + "dependencies": { + "@cspell/cspell-pipe": "9.1.3", + "@cspell/cspell-types": "9.1.3" + }, + "bin": { + "cspell-grammar": "bin.mjs" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-io": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.1.3.tgz", + "integrity": "sha512-fdgAVrthOY1pPsBZHWVjEVn6uHMAshj2n75eu2rvUd6EcmMuLR13EcIXHoMcQo/1Az05x2UgG7HuK+0MuRcikQ==", + "dev": true, + "dependencies": { + "@cspell/cspell-service-bus": "9.1.3", + "@cspell/url": "9.1.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-lib": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.1.3.tgz", + "integrity": "sha512-egESsnErAPtC/wuqbHWW28eRKChkg5h+vFQQuZ0iThuOSZ65jeSM0ESOt8W3TH2JD7EGo2pvPED/7rZjjnMIcQ==", + "dev": true, + "dependencies": { + "@cspell/cspell-bundled-dicts": "9.1.3", + "@cspell/cspell-pipe": "9.1.3", + "@cspell/cspell-resolver": "9.1.3", + "@cspell/cspell-types": "9.1.3", + "@cspell/dynamic-import": "9.1.3", + "@cspell/filetypes": "9.1.3", + "@cspell/strong-weak-map": "9.1.3", + "@cspell/url": "9.1.3", + "clear-module": "^4.1.2", + "comment-json": "^4.2.5", + "cspell-config-lib": "9.1.3", + "cspell-dictionary": "9.1.3", + "cspell-glob": "9.1.3", + "cspell-grammar": "9.1.3", + "cspell-io": "9.1.3", + "cspell-trie-lib": "9.1.3", + "env-paths": "^3.0.0", + "fast-equals": "^5.2.2", + "gensequence": "^7.0.0", + "import-fresh": "^3.3.1", + "resolve-from": "^5.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-uri": "^3.1.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-lib/node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cspell-trie-lib": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.1.3.tgz", + "integrity": "sha512-fvI0ede/rPr+SB0zX8le426c5lroNdmMTkl4fFk2e0w5/JZRHIfkuenhWe0MZeb18d1NPRIiLgxoD87zswLynw==", + "dev": true, + "dependencies": { + "@cspell/cspell-pipe": "9.1.3", + "@cspell/cspell-types": "9.1.3", + "gensequence": "^7.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.178", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.178.tgz", + "integrity": "sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/env-ci": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.0.tgz", + "integrity": "sha512-Z8dnwSDbV1XYM9SBF2J0GcNVvmfmfh3a49qddGIROhBoVro6MZVTji15z/sJbQ2ko2ei8n988EU1wzoLU/tF+g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/env-ci/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.4.tgz", + "integrity": "sha512-dDLGjnP2cKbEppxVICxI/Uf4YemmGMPNy0QytCbfafbpYk9AFQsxb8Uyrxii0RPK7FWgLGlSem+07WirwS3cFQ==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "30.0.4", + "@jest/get-type": "30.0.1", + "jest-matcher-utils": "30.0.4", + "jest-message-util": "30.0.2", + "jest-mock": "30.0.2", + "jest-util": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", + "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gensequence": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-7.0.0.tgz", + "integrity": "sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, + "node_modules/git-log-parser/node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from-esm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", + "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=18.20" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer": { + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.7.0.tgz", + "integrity": "sha512-KKFRc++IONSyE2UYw9CJ1V0IWx5yQKomwB+pp3cWomWs+v2+ZsG11G2OVfAjFS6WWCppKw+RfKmpqGfSzD5QBQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/prompts": "^7.6.0", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "mute-stream": "^2.0.0", + "run-async": "^4.0.4", + "rxjs": "^7.8.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jest": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.4.tgz", + "integrity": "sha512-9QE0RS4WwTj/TtTC4h/eFVmFAhGNVerSB9XpJh8sqaXlP73ILcPcZ7JWjjEtJJe2m8QyBLKKfPQuK+3F+Xij/g==", + "dev": true, + "dependencies": { + "@jest/core": "30.0.4", + "@jest/types": "30.0.1", + "import-local": "^3.2.0", + "jest-cli": "30.0.4" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.2.tgz", + "integrity": "sha512-Ius/iRST9FKfJI+I+kpiDh8JuUlAISnRszF9ixZDIqJF17FckH5sOzKC8a0wd0+D+8em5ADRHA5V5MnfeDk2WA==", + "dev": true, + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.0.2", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.4.tgz", + "integrity": "sha512-o6UNVfbXbmzjYgmVPtSQrr5xFZCtkDZGdTlptYvGFSN80RuOOlTe73djvMrs+QAuSERZWcHBNIOMH+OEqvjWuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.4", + "@jest/expect": "30.0.4", + "@jest/test-result": "30.0.4", + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.0.2", + "jest-matcher-utils": "30.0.4", + "jest-message-util": "30.0.2", + "jest-runtime": "30.0.4", + "jest-snapshot": "30.0.4", + "jest-util": "30.0.2", + "p-limit": "^3.1.0", + "pretty-format": "30.0.2", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.4.tgz", + "integrity": "sha512-3dOrP3zqCWBkjoVG1zjYJpD9143N9GUCbwaF2pFF5brnIgRLHmKcCIw+83BvF1LxggfMWBA0gxkn6RuQVuRhIQ==", + "dev": true, + "dependencies": { + "@jest/core": "30.0.4", + "@jest/test-result": "30.0.4", + "@jest/types": "30.0.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.0.4", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.4.tgz", + "integrity": "sha512-3dzbO6sh34thAGEjJIW0fgT0GA0EVlkski6ZzMcbW6dzhenylXAE/Mj2MI4HonroWbkKc6wU6bLVQ8dvBSZ9lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.0.1", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.0.4", + "@jest/types": "30.0.1", + "babel-jest": "30.0.4", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.0.4", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.0.4", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.2", + "jest-runner": "30.0.4", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.0.2", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.4.tgz", + "integrity": "sha512-TSjceIf6797jyd+R64NXqicttROD+Qf98fex7CowmlSn7f8+En0da1Dglwr1AXxDtVizoxXYZBlUQwNhoOXkNw==", + "dev": true, + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", + "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", + "dev": true, + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.2.tgz", + "integrity": "sha512-ZFRsTpe5FUWFQ9cWTMguCaiA6kkW5whccPy9JjD1ezxh+mJeqmz8naL8Fl/oSbNJv3rgB0x87WBIkA5CObIUZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "@jest/types": "30.0.1", + "chalk": "^4.1.2", + "jest-util": "30.0.2", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.4.tgz", + "integrity": "sha512-p+rLEzC2eThXqiNh9GHHTC0OW5Ca4ZfcURp7scPjYBcmgpR9HG6750716GuUipYf2AcThU3k20B31USuiaaIEg==", + "dev": true, + "dependencies": { + "@jest/environment": "30.0.4", + "@jest/fake-timers": "30.0.4", + "@jest/types": "30.0.1", + "@types/node": "*", + "jest-mock": "30.0.2", + "jest-util": "30.0.2", + "jest-validate": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.2.tgz", + "integrity": "sha512-telJBKpNLeCb4MaX+I5k496556Y2FiKR/QLZc0+MGBYl4k3OO0472drlV2LUe7c1Glng5HuAu+5GLYp//GpdOQ==", + "dev": true, + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.2", + "jest-worker": "30.0.2", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.2.tgz", + "integrity": "sha512-U66sRrAYdALq+2qtKffBLDWsQ/XoNNs2Lcr83sc9lvE/hEpNafJlq2lXCPUBMNqamMECNxSIekLfe69qg4KMIQ==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.0.1", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.4.tgz", + "integrity": "sha512-ubCewJ54YzeAZ2JeHHGVoU+eDIpQFsfPQs0xURPWoNiO42LGJ+QGgfSf+hFIRplkZDkhH5MOvuxHKXRTUU3dUQ==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "jest-diff": "30.0.4", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.2.tgz", + "integrity": "sha512-vXywcxmr0SsKXF/bAD7t7nMamRvPuJkras00gqYeB1V0WllxZrbZ0paRr3XqpFU2sYYjD0qAaG2fRyn/CGZ0aw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.0.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.0.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.2.tgz", + "integrity": "sha512-PnZOHmqup/9cT/y+pXIVbbi8ID6U1XHRmbvR7MvUy4SLqhCbwpkmXhLbsWbGewHrV5x/1bF7YDjs+x24/QSvFA==", + "dev": true, + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "jest-util": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.2.tgz", + "integrity": "sha512-q/XT0XQvRemykZsvRopbG6FQUT6/ra+XV6rPijyjT6D0msOyCvR2A5PlWZLd+fH0U8XWKZfDiAgrUNDNX2BkCw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.4.tgz", + "integrity": "sha512-EQBYow19B/hKr4gUTn+l8Z+YLlP2X0IoPyp0UydOtrcPbIOYzJ8LKdFd+yrbwztPQvmlBFUwGPPEzHH1bAvFAw==", + "dev": true, + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.0.4" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.4.tgz", + "integrity": "sha512-mxY0vTAEsowJwvFJo5pVivbCpuu6dgdXRmt3v3MXjBxFly7/lTk3Td0PaMyGOeNQUFmSuGEsGYqhbn7PA9OekQ==", + "dev": true, + "dependencies": { + "@jest/console": "30.0.4", + "@jest/environment": "30.0.4", + "@jest/test-result": "30.0.4", + "@jest/transform": "30.0.4", + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.0.4", + "jest-haste-map": "30.0.2", + "jest-leak-detector": "30.0.2", + "jest-message-util": "30.0.2", + "jest-resolve": "30.0.2", + "jest-runtime": "30.0.4", + "jest-util": "30.0.2", + "jest-watcher": "30.0.4", + "jest-worker": "30.0.2", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.4.tgz", + "integrity": "sha512-tUQrZ8+IzoZYIHoPDQEB4jZoPyzBjLjq7sk0KVyd5UPRjRDOsN7o6UlvaGF8ddpGsjznl9PW+KRgWqCNO+Hn7w==", + "dev": true, + "dependencies": { + "@jest/environment": "30.0.4", + "@jest/fake-timers": "30.0.4", + "@jest/globals": "30.0.4", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.0.4", + "@jest/transform": "30.0.4", + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "jest-message-util": "30.0.2", + "jest-mock": "30.0.2", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.2", + "jest-snapshot": "30.0.4", + "jest-util": "30.0.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.4.tgz", + "integrity": "sha512-S/8hmSkeUib8WRUq9pWEb5zMfsOjiYWDWzFzKnjX7eDyKKgimsu9hcmsUEg8a7dPAw8s/FacxsXquq71pDgPjQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.0.4", + "@jest/get-type": "30.0.1", + "@jest/snapshot-utils": "30.0.4", + "@jest/transform": "30.0.4", + "@jest/types": "30.0.1", + "babel-preset-current-node-syntax": "^1.1.0", + "chalk": "^4.1.2", + "expect": "30.0.4", + "graceful-fs": "^4.2.11", + "jest-diff": "30.0.4", + "jest-matcher-utils": "30.0.4", + "jest-message-util": "30.0.2", + "jest-util": "30.0.2", + "pretty-format": "30.0.2", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.2.tgz", + "integrity": "sha512-8IyqfKS4MqprBuUpZNlFB5l+WFehc8bfCe1HSZFHzft2mOuND8Cvi9r1musli+u6F3TqanCZ/Ik4H4pXUolZIg==", + "dev": true, + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.2.tgz", + "integrity": "sha512-noOvul+SFER4RIvNAwGn6nmV2fXqBq67j+hKGHKGFCmK4ks/Iy1FSrqQNBLGKlu4ZZIRL6Kg1U72N1nxuRCrGQ==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.0.1", + "@jest/types": "30.0.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.0.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.4.tgz", + "integrity": "sha512-YESbdHDs7aQOCSSKffG8jXqOKFqw4q4YqR+wHYpR5GWEQioGvL0BfbcjvKIvPEM0XGfsfJrka7jJz3Cc3gI4VQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "30.0.4", + "@jest/types": "30.0.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.0.2", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.2.tgz", + "integrity": "sha512-RN1eQmx7qSLFA+o9pfJKlqViwL5wt+OL3Vff/A+/cPsmuw7NPwfgl33AP+/agRmHzPOFgXviRycR9kYwlcRQXg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.0.2", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lefthook": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.11.16.tgz", + "integrity": "sha512-NbFZaAJUEiwBv6Npg7TZOCo9Bxh8VUSuBZ55CTulH9roQjknSXQWgGYz9FaHvqVeMBf7Xog2Wk84Ce7gWrWlYw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "lefthook": "bin/index.js" + }, + "optionalDependencies": { + "lefthook-darwin-arm64": "1.11.16", + "lefthook-darwin-x64": "1.11.16", + "lefthook-freebsd-arm64": "1.11.16", + "lefthook-freebsd-x64": "1.11.16", + "lefthook-linux-arm64": "1.11.16", + "lefthook-linux-x64": "1.11.16", + "lefthook-openbsd-arm64": "1.11.16", + "lefthook-openbsd-x64": "1.11.16", + "lefthook-windows-arm64": "1.11.16", + "lefthook-windows-x64": "1.11.16" + } + }, + "node_modules/lefthook-darwin-arm64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.11.16.tgz", + "integrity": "sha512-ThsVyHPexHax7aZYpFbsswk5HPR2ap3zsJ1yE189Y97U+tLEHS+Mq98MqC1U1zFm0Ts42gpGDBx6chIRjx7Uig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/lefthook-darwin-x64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.11.16.tgz", + "integrity": "sha512-P3QtUJ/ICX3l+ZJqqKGyovT6Hi2N60Vt8znGglJ+OeCpVMc6H6N0ID37+jHlnyjO9AfL4EkAj70vwYe8Fzfjsw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/lefthook-freebsd-arm64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.11.16.tgz", + "integrity": "sha512-X0EyVoCg4+nczJDZ0rc0gpZHOrLr4Nk7knoYFKrBnvutuMCs9ixU+tA4sV8pu4KT13gigbXkxjKlIN7QUqFCxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/lefthook-freebsd-x64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.11.16.tgz", + "integrity": "sha512-m2nfcpz2i4AdQ9fZqcoTCxAFotqXHYzUThgqcRyMaPHjlpy+ZmVnopppt9MS4GfDDq+dqjka28ZZObE7T1Nb5g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/lefthook-linux-arm64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.11.16.tgz", + "integrity": "sha512-mXwCXs5J1qyq6QmBXFy2/YYG0sCzz1C0sfnzLdgXoAAmhEFfA4wy/DX/jNbvMPvRLfKHiXtf3eaqjLEYg9CGjQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/lefthook-linux-x64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.11.16.tgz", + "integrity": "sha512-MW1ClFIk3uTfpOCm+FGW8NZbXWA6Xdf/eUw6//mEVs3Q4EsZ4GYZn0AFRMrsaXxbSZnvAPhtNoEVqRfYWjWKBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/lefthook-openbsd-arm64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.11.16.tgz", + "integrity": "sha512-6Aqjyc2TkkEpogsUvzLSdMAy/fN6YHlq3XA47N7VNc0Ke0XP/XIImb0zEPsYDjrdNqZbEKoXn+IYkValtS5DXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/lefthook-openbsd-x64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.11.16.tgz", + "integrity": "sha512-ierlKlnrUe1ML6F3JFgFllgy71qY1S5I2wOclI3yh2EGykAJIUmdkgz/f0KT1slV2aXeSP+QgBTu496WzZSjIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/lefthook-windows-arm64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.11.16.tgz", + "integrity": "sha512-AAjDKWOExTS1XlSvNNIa3YIJbf90SZ5X0NSA7EgHobegadLcLrkl3aX+2zcw+yvpm1AOF0WUZdYxkAHL5MNQOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/lefthook-windows-x64": { + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.11.16.tgz", + "integrity": "sha512-qOEAinMMV5rlf4C0VPSIlPaj5nh2CD4lzAv7+nAUydDiDQcVkkPbiwCaRkSVX509k6SctDCYQhtBnG/bwdREFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-terminal": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", + "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.4.1", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.2.0", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <16" + } + }, + "node_modules/marked-terminal/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", + "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "peer": true, + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", + "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", + "dev": true, + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.9.3", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.3.tgz", + "integrity": "sha512-6Eh1u5Q+kIVXeA8e7l2c/HpnFFcwrkt37xDMujD5be1gloWa9p6j3Fsv3mByXXmqJHy+2cElRMML8opNT7xIJQ==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.1", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.2.0", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.2.2", + "@npmcli/run-script": "^9.1.0", + "@sigstore/tuf": "^3.1.1", + "abbrev": "^3.0.1", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.2.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.1.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.1", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.1", + "libnpmexec": "^9.0.1", + "libnpmfund": "^6.0.1", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.1", + "libnpmpublish": "^10.0.1", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.2.0", + "nopt": "^8.1.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.2", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.1", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.4.3", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.1", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.14", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oxlint": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.6.0.tgz", + "integrity": "sha512-jtaD65PqzIa1udvSxxscTKBxYKuZoFXyKGLiU1Qjo1ulq3uv/fQDtoV1yey1FrQZrQjACGPi1Widsy1TucC7Jg==", + "license": "MIT", + "bin": { + "oxc_language_server": "bin/oxc_language_server", + "oxlint": "bin/oxlint" + }, + "engines": { + "node": ">=8.*" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/darwin-arm64": "1.6.0", + "@oxlint/darwin-x64": "1.6.0", + "@oxlint/linux-arm64-gnu": "1.6.0", + "@oxlint/linux-arm64-musl": "1.6.0", + "@oxlint/linux-x64-gnu": "1.6.0", + "@oxlint/linux-x64-musl": "1.6.0", + "@oxlint/win32-arm64": "1.6.0", + "@oxlint/win32-x64": "1.6.0" + } + }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-map": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", + "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", + "dev": true, + "dependencies": { + "callsites": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.2.tgz", + "integrity": "sha512-yC5/EBSOrTtqhCKfLHqoUIAXVRZnukHPwWBJWR7h84Q3Be1DRQZLncwcfLoPA5RPQ65qfiCMqgYwdUuQ//eVpg==", + "dev": true, + "dependencies": { + "@jest/schemas": "30.0.1", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/type-fest": { + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.1.tgz", + "integrity": "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.1.tgz", + "integrity": "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.4.tgz", + "integrity": "sha512-2cgeRHnV11lSXBEhq7sN7a5UVjTKm9JTb9x8ApIT//16D7QL96AgnNeWSGoB4gIHc0iYw/Ha0Z+waBaCYZVNhg==", + "license": "MIT", + "dependencies": { + "oxlint": "^1.2.0", + "prettier": "^3.5.3" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semantic-release": { + "version": "24.2.3", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.3.tgz", + "integrity": "sha512-KRhQG9cUazPavJiJEFIJ3XAMjgfd0fcK3B+T26qOl8L0UG5aZUjeRfREO0KM5InGtYwxqiiytkJrbcYoLDEv0A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@semantic-release/commit-analyzer": "^13.0.0-beta.1", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^11.0.0", + "@semantic-release/npm": "^12.0.0", + "@semantic-release/release-notes-generator": "^14.0.0-beta.1", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^9.0.0", + "debug": "^4.0.0", + "env-ci": "^11.0.0", + "execa": "^9.0.0", + "figures": "^6.0.0", + "find-versions": "^6.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^8.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "marked": "^12.0.0", + "marked-terminal": "^7.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-package-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=20.8.1" + } + }, + "node_modules/semantic-release/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/semantic-release/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0", + "peer": true + }, + "node_modules/semantic-release/node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/semantic-release/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/execa": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", + "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/semantic-release/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/semantic-release/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/smol-toml": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.1.tgz", + "integrity": "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/super-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", + "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "function-timeout": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.2.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ts-jest": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", + "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "devOptional": true + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.0.tgz", + "integrity": "sha512-uw3hCGO/RdAEAb4zgJ3C/v6KIAFFOtBoxR86b2Ejc5TnH7HrhTWJR2o0A9ullC3eWMegKQCw/arQ/JivywQzkg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.0", + "@unrs/resolver-binding-android-arm64": "1.11.0", + "@unrs/resolver-binding-darwin-arm64": "1.11.0", + "@unrs/resolver-binding-darwin-x64": "1.11.0", + "@unrs/resolver-binding-freebsd-x64": "1.11.0", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.0", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.0", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.0", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.0", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.0", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-x64-musl": "1.11.0", + "@unrs/resolver-binding-wasm32-wasi": "1.11.0", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.0", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.0", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } } diff --git a/package.json b/package.json index 54e5d62..a5f549b 100644 --- a/package.json +++ b/package.json @@ -17,22 +17,24 @@ } }, "scripts": { - "dev": "npx nodemon", - "build": "rimraf ./build && tsc", + "dev": "tsc -p tsconfig.build.json --watch", + "build": "rimraf ./build && tsc -p tsconfig.build.json", + "build:watch": "tsc -p tsconfig.build.json --watch", "start": "npm run build && node build/index.js", - "test": "jest --detectOpenHandles --forceExit", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage", - "lint": "biome lint", - "lint:fix": "biome lint --fix", - "format": "biome format", - "format:write": "biome format --write", - "format:check": "biome format --check", - "typecheck": "tsc --noEmit", - "spellcheck": "cspell \"**/*.{js,ts,md,json,yml,yaml}\"" + "test": "jest --detectOpenHandles --passWithNoTests", + "test:watch": "jest --watch --passWithNoTests", + "test:coverage": "jest --coverage --passWithNoTests", + "lint": "npx @biomejs/biome lint --write .", + "format": "npx @biomejs/biome format --write .", + "check:unsafe": "npx @biomejs/biome check --fix --unsafe", + "typecheck": "tsc --noEmit -p tsconfig.json", + "spellcheck": "cspell \"**/*.{js,ts,md,json,yml,yaml}\"", + "ci": "npm run typecheck && npm run lint && npm run test:coverage", + "clean": "rimraf build coverage .nyc_output" }, "devDependencies": { "@biomejs/biome": "2.1.1", + "@commander-js/extra-typings": "^14.0.0", "@commitlint/cli": "^19.8.0", "@commitlint/config-conventional": "^19.8.0", "@cspell/dict-software-terms": "^5.0.7", @@ -40,20 +42,26 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@semantic-release/npm": "^12.0.1", + "@types/inquirer": "^9.0.8", "@types/node": "^24.0.8", "@types/pluralize": "^0.0.33", "cspell": "^9.0.0", "jest": "^30.0.3", "lefthook": "^1.11.11", - "nodemon": "^3.1.10", "rimraf": "^6.0.1", "ts-jest": "^29.3.2", "ts-node": "^10.9.2", - "typescript": "^5.8.3" + "typescript": "^5.6.3" }, "dependencies": { "@datocms/cma-client-node": "^4.0.1", + "@inquirer/prompts": "^7.6.0", + "commander": "^14.0.0", + "eslint": "^9.31.0", + "glob": "^11.0.3", + "inquirer": "^12.7.0", "pluralize": "^8.0.0", - "tsconfig-paths": "^4.2.0" + "prettier": "^3.6.2", + "tsx": "^4.20.3" } } diff --git a/project-words.txt b/project-words.txt index e9a7b7f..fca047e 100644 --- a/project-words.txt +++ b/project-words.txt @@ -13,4 +13,7 @@ ESM upserting informations childrens -npmignore \ No newline at end of file +npmignore +esbuild +fieldsets +lefthook \ No newline at end of file diff --git a/src/Api/DatoApi.ts b/src/Api/DatoApi.ts index c746c77..581f8ec 100644 --- a/src/Api/DatoApi.ts +++ b/src/Api/DatoApi.ts @@ -1,24 +1,53 @@ -import { ApiError } from "@datocms/cma-client-node"; -import type { getDatoClient } from "../config"; +import { ApiError, type Client } from "@datocms/cma-client-node"; +import { ConsoleLogger } from "../logger"; import { parseApiError, throwMappedApiError } from "./apiErrorHandler"; export default class DatoApi { - constructor(readonly client: ReturnType) {} + private logger: ConsoleLogger; + + constructor(readonly client: Client, logger?: ConsoleLogger) { + this.logger = logger || new ConsoleLogger(); + } public async call( fn: () => Promise, retries = 3, backoffMs = 500, + operation?: string, + requestData?: any, ): Promise { + const startTime = Date.now(); + const operationName = operation || 'unknown'; + + this.logger.debug(`API Call Start: ${operationName}`); + if (requestData) { + this.logger.debugJson(`API Request Data: ${operationName}`, requestData); + } + let lastErr: unknown; for (let i = 1; i <= retries; i++) { try { - return await fn(); + const result = await fn(); + const duration = Date.now() - startTime; + this.logger.debug(`API Call End: ${operationName} (${duration}ms)`); + + // Log response data (but truncate large responses) + if (Array.isArray(result) && result.length > 0) { + this.logger.debugJson(`API Response: ${operationName} (${result.length} items)`, { + count: result.length, + sample: result[0], + }); + } else if (result && typeof result === 'object') { + this.logger.debugJson(`API Response: ${operationName}`, result); + } + + return result; } catch (err: unknown) { // Retry on transient CMA errors if (err instanceof ApiError) { const first = err.errors[0]?.attributes; if (first?.transient && i < retries) { + this.logger.debug(`API Retry: ${operationName} (attempt ${i}, transient error)`); await new Promise((r) => setTimeout(r, backoffMs * i)); continue; } @@ -27,6 +56,14 @@ export default class DatoApi { // For non-transient or other errors, map CMA errors or capture last error const parsed = parseApiError(err); if (parsed) { + const duration = Date.now() - startTime; + this.logger.debug(`API Error: ${operationName} (${duration}ms) - ${(err as Error).message}`); + if (err instanceof ApiError) { + this.logger.debugJson(`API Error Details: ${operationName}`, { + message: err.message, + errors: err.errors, + }); + } throwMappedApiError(err); } @@ -34,6 +71,9 @@ export default class DatoApi { break; } } + + const duration = Date.now() - startTime; + this.logger.debug(`API Error: ${operationName} (${duration}ms) - ${(lastErr as Error).message}`); throw lastErr; } } diff --git a/src/Api/apiErrorHandler.ts b/src/Api/apiErrorHandler.ts index 64ef1f7..cc81f7a 100644 --- a/src/Api/apiErrorHandler.ts +++ b/src/Api/apiErrorHandler.ts @@ -55,12 +55,13 @@ export function parseApiError(err: unknown): ParsedError | null { const entities = getErrorEntities(err); if (!entities?.length) return null; - const { - code: outerCode, - doc_url: docUrl, - details, - transient, - } = entities[0].attributes; + const firstEntity = entities[0]; + if (!firstEntity) return null; + + const attributes = firstEntity.attributes; + if (!attributes) return null; + + const { code: outerCode, doc_url: docUrl, details, transient } = attributes; // If `details` itself has a `code` field, treat that as innerCode const innerCode = diff --git a/src/BlockBuilder.ts b/src/BlockBuilder.ts index 92db5b2..b04fbc2 100644 --- a/src/BlockBuilder.ts +++ b/src/BlockBuilder.ts @@ -1,7 +1,5 @@ -import ItemTypeBuilder, { - type ItemTypeBuilderConfig, - type ItemTypeBuilderType, -} from "./ItemTypeBuilder"; +import ItemTypeBuilder, { type ItemTypeBuilderType } from "./ItemTypeBuilder"; +import type { DatoBuilderConfig } from "./types/DatoBuilderConfig"; type BlockBuilderBody = { /** @@ -16,14 +14,23 @@ type BlockBuilderBody = { hint?: string | null; }; +type BlockBuilderOptions = { + name: string; + options?: BlockBuilderBody; + config: Required; +}; + export default class BlockBuilder extends ItemTypeBuilder { - public type: ItemTypeBuilderType = "block"; + public override type: ItemTypeBuilderType = "block"; - constructor( - name: string, - options?: BlockBuilderBody, - config?: ItemTypeBuilderConfig, - ) { - super("block", { ...options, name }, config); + constructor({ name, options, config }: BlockBuilderOptions) { + super({ + type: "block", + body: { + ...options, + name, + }, + config, + }); } } diff --git a/src/DatoCmsSync.ts b/src/DatoCmsSync.ts new file mode 100644 index 0000000..e1eb3f4 --- /dev/null +++ b/src/DatoCmsSync.ts @@ -0,0 +1,396 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { buildClient } from "@datocms/cma-client-node"; +import { confirm } from "@inquirer/prompts"; +import { FieldGeneratorError } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import { FileGenerationService } from "@/FileGeneration/FileGenerationService"; +import DatoApi from "./Api/DatoApi"; +import type { CacheManager } from "./cache/CacheManager"; +import type { ConsoleLogger } from "./logger"; +import type { DatoBuilderConfig } from "./types/DatoBuilderConfig"; + +/** + * Custom error for DatoCMS sync operations + */ +export class DatoSyncError extends Error { + constructor( + message: string, + public readonly operation: string, + public readonly itemTypeId?: string, + public readonly cause?: Error, + ) { + super( + `DatoCMS Sync [${operation}${itemTypeId ? ` - ${itemTypeId}` : ""}]: ${message}`, + ); + this.name = "DatoSyncError"; + } +} + +// TODO: When everything is stable, make this be able to run in concurrent mode like the run command + +export interface SyncOptions { + config: Required; + cache: CacheManager; + logger: ConsoleLogger; + dryRun?: boolean; + force?: boolean; + localDevelopment?: boolean; +} + +export class DatoCmsSync { + private readonly api: DatoApi; + private readonly logger: ConsoleLogger; + private readonly config: Required; + private readonly cache: CacheManager; + private readonly fileGenerationService: FileGenerationService; + private readonly isLocalDevelopment: boolean; + + constructor(options: SyncOptions) { + this.config = options.config; + this.logger = options.logger; + this.cache = options.cache; + this.api = new DatoApi( + buildClient({ apiToken: this.config.apiToken }), + this.logger, + ); + this.fileGenerationService = new FileGenerationService(); + this.isLocalDevelopment = options.localDevelopment ?? false; + } + + /** + * Fetch all item types from DatoCMS + */ + async fetchItemTypes() { + this.logger.trace("Fetching item types from DatoCMS"); + + try { + const response = await this.api.call( + () => this.api.client.itemTypes.list(), + 3, + 500, + "itemTypes.list()", + ); + this.logger.debug(`Fetched ${response.length} item types from DatoCMS`); + return response; + } catch (error) { + const message = error instanceof Error ? error.message : "Unknown error"; + this.logger.error(`Failed to fetch item types: ${message}`); + throw new DatoSyncError( + `Failed to fetch item types: ${message}`, + "fetchItemTypes", + undefined, + error instanceof Error ? error : undefined, + ); + } + } + + /** + * Fetch fields for a specific item type + */ + async fetchFields(itemTypeId: string) { + this.logger.trace(`Fetching fields for item type: ${itemTypeId}`); + + try { + const fields = await this.api.call( + () => this.api.client.fields.list(itemTypeId), + 3, + 500, + `fields.list(${itemTypeId})`, + ); + this.logger.trace( + `Fetched ${fields.length} fields for item type: ${itemTypeId}`, + ); + return fields; + } catch (error) { + const message = error instanceof Error ? error.message : "Unknown error"; + this.logger.error( + `Failed to fetch fields for item type ${itemTypeId}: ${message}`, + ); + throw new DatoSyncError( + `Failed to fetch fields: ${message}`, + "fetchFields", + itemTypeId, + error instanceof Error ? error : undefined, + ); + } + } + + /** + * Separate blocks and models from item types + */ + private categorizeItemTypes(itemTypes: ItemType[]) { + const blocks = itemTypes.filter((item) => item.modular_block); + const models = itemTypes.filter((item) => !item.modular_block); + + this.logger.debug( + `Categorized ${blocks.length} blocks and ${models.length} models`, + ); + + return { blocks, models }; + } + + /** + * Check if sync is needed based on cache + */ + private async shouldSync( + itemType: ItemType, + fields: Field[], + force: boolean, + ): Promise { + if (force) { + this.logger.trace(`Force sync enabled for ${itemType.api_key}`); + return true; + } + + const cacheKey = `sync:${itemType.api_key}`; + const cached = this.cache.get(cacheKey); + + if (!cached) { + this.logger.trace(`No cache found for ${itemType.api_key}`); + return true; + } + + // Create a hash of the current state + const currentHash = this.createHash({ itemType, fields }); + + if (currentHash !== cached.hash) { + this.logger.trace(`Hash mismatch for ${itemType.api_key}, sync needed`); + return true; + } + + this.logger.trace(`Cache hit for ${itemType.api_key}, skipping sync`); + return false; + } + + /** + * Create a hash for caching purposes + */ + private createHash(data: { itemType: ItemType; fields: Field[] }): string { + const crypto = require("node:crypto"); + return crypto + .createHash("sha256") + .update(JSON.stringify(data)) + .digest("hex"); + } + + /** + * Update cache after successful sync + */ + private async updateCache(itemType: ItemType, fields: Field[]) { + const cacheKey = `sync:${itemType.api_key}`; + const hash = this.createHash({ itemType, fields }); + + await this.cache.set(cacheKey, { + id: itemType.id, + hash, + }); + + this.logger.trace(`Updated cache for ${itemType.api_key}`); + } + + /** + * Main sync method + */ + async sync( + options: { dryRun?: boolean; force?: boolean } = {}, + ): Promise { + this.logger.info("Starting sync from DatoCMS to local files"); + + try { + // Fetch all item types + const itemTypes = await this.fetchItemTypes(); + + if (itemTypes.length === 0) { + this.logger.warn("No item types found in DatoCMS"); + return; + } + + // Set item type references for the file generator + this.fileGenerationService.setItemTypeReferences(itemTypes); + + // Categorize into blocks and models + const { blocks, models } = this.categorizeItemTypes(itemTypes); + + this.logger.info( + `Found ${blocks.length} blocks and ${models.length} models`, + ); + + // Process blocks + if (blocks.length > 0) { + this.logger.info("Processing blocks..."); + await this.processItemTypes(blocks, "block", options); + } + + // Process models + if (models.length > 0) { + this.logger.info("Processing models..."); + await this.processItemTypes(models, "model", options); + } + + this.logger.success("Sync completed successfully!"); + } catch (error) { + this.logger.error(`Sync failed: ${(error as Error).message}`); + throw error; + } + } + + /** + * Process a list of item types (blocks or models) + */ + private async processItemTypes( + itemTypes: ItemType[], + type: "block" | "model", + options: { dryRun?: boolean; force?: boolean }, + ): Promise { + const results = []; + + for (const itemType of itemTypes) { + try { + this.logger.debug( + `Processing ${type}: ${itemType.name} (${itemType.api_key})`, + ); + + // Fetch fields for this item type + const fields = await this.fetchFields(itemType.id); + + // Check if sync is needed + const needsSync = await this.shouldSync( + itemType, + fields, + options.force || false, + ); + + if (!needsSync) { + this.logger.debug( + `Skipping ${itemType.api_key} - no changes detected`, + ); + continue; + } + + if (options.dryRun) { + this.logger.info(`[DRY RUN] Would sync ${type}: ${itemType.name}`); + results.push({ type, name: itemType.name, action: "would-sync" }); + continue; + } + + // Generate file content + let fileContent: string; + try { + fileContent = await this.fileGenerationService.generateFile({ + itemType, + fields, + type, + localDevelopment: this.isLocalDevelopment, + }); + } catch (error) { + if (error instanceof FieldGeneratorError) { + throw new DatoSyncError( + `Field generation failed: ${error.message}`, + "generateFile", + itemType.id, + error, + ); + } + throw error; + } + + // Determine file path + const filePath = this.getFilePath(itemType, type); + + // Ensure directory exists + await fs.mkdir(path.dirname(filePath), { recursive: true }); + + // Write file + try { + await fs.writeFile(filePath, fileContent, "utf8"); + } catch (error) { + const message = + error instanceof Error ? error.message : "Unknown error"; + throw new DatoSyncError( + `Failed to write file ${filePath}: ${message}`, + "writeFile", + itemType.id, + error instanceof Error ? error : undefined, + ); + } + + // Update cache + await this.updateCache(itemType, fields); + + this.logger.success(`Generated ${type}: ${filePath}`); + results.push({ type, name: itemType.name, action: "synced", filePath }); + } catch (error) { + const message = + error instanceof Error ? error.message : "Unknown error"; + this.logger.error( + `Failed to process ${type} ${itemType.name}: ${message}`, + ); + + // Log additional context for DatoSyncError + if (error instanceof DatoSyncError) { + this.logger.debug( + `Operation: ${error.operation}, ItemType: ${error.itemTypeId}`, + ); + } + + results.push({ + type, + name: itemType.name, + action: "failed", + error: message, + }); + } + } + + // Summary + const synced = results.filter((r) => r.action === "synced").length; + const failed = results.filter((r) => r.action === "failed").length; + const skipped = itemTypes.length - synced - failed; + + this.logger.info( + `${type.charAt(0).toUpperCase() + type.slice(1)} summary: ${synced} synced, ${skipped} skipped, ${failed} failed`, + ); + } + + /** + * Get the file path for an item type + */ + private getFilePath(itemType: ItemType, type: "block" | "model"): string { + const basePath = + type === "block" + ? this.config.syncBlocksPath + : this.config.syncModelsPath; + const fileName = `${this.toPascalCase(itemType.name)}.ts`; + return path.resolve(process.cwd(), basePath, fileName); + } + + /** + * Convert string to PascalCase + */ + private toPascalCase(str: string): string { + return str + .replace(/[^a-zA-Z0-9\s]/g, " ") + .split(/\s+/) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(""); + } + + /** + * Ask user for confirmation before proceeding + */ + async confirmSync(force: boolean = false): Promise { + if (force) { + return true; + } + + return confirm({ + message: + "This will overwrite existing local files. Are you sure you want to continue?", + default: false, + }); + } +} diff --git a/src/Fields/AssetGallery.ts b/src/Fields/AssetGallery.ts index a3bcbe2..4c80bc2 100644 --- a/src/Fields/AssetGallery.ts +++ b/src/Fields/AssetGallery.ts @@ -1,5 +1,5 @@ import type { ValidatorConfig } from "../Validators/Validators"; -import Field, { type FieldBody } from "./Field"; //size, file_size, image_dimensions, image_aspect_ratio, extension, required_alt_title +import Field, { type FieldBody } from "./Field"; export type AssetGalleryBody = Omit & { validators?: Pick< diff --git a/src/Fields/Date.ts b/src/Fields/Date.ts index 8fde34a..258a992 100644 --- a/src/Fields/Date.ts +++ b/src/Fields/Date.ts @@ -1,4 +1,4 @@ -import type { ValidatorConfig } from "../Validators/Validators"; +import type { ValidatorConfig } from "@/Validators/Validators"; import Field, { type FieldBody } from "./Field"; export type DateBody = Omit & { diff --git a/tests/fields/field.test.ts b/src/Fields/Field.test.ts similarity index 100% rename from tests/fields/field.test.ts rename to src/Fields/Field.test.ts diff --git a/tests/fields/integer.test.ts b/src/Fields/Integer.test.ts similarity index 100% rename from tests/fields/integer.test.ts rename to src/Fields/Integer.test.ts diff --git a/src/FileGeneration/FieldGenerators/BooleanFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/BooleanFieldGenerator.test.ts new file mode 100644 index 0000000..36944ae --- /dev/null +++ b/src/FileGeneration/FieldGenerators/BooleanFieldGenerator.test.ts @@ -0,0 +1,173 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { BooleanConfig } from "@/Fields/Boolean"; +import { BooleanFieldGenerator } from "@/FileGeneration/FieldGenerators/BooleanFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "boolean", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("BooleanFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a boolean field with label", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Is Active", + api_key: "is_active", + }), + }); + + expect(booleanGenerator.generateBuildConfig()).toEqual({ + label: "Is Active", + body: { + api_key: "is_active", + }, + } satisfies BooleanConfig); + }); + + it("returns correct method call name", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(booleanGenerator.getMethodCallName()).toBe("addBoolean"); + }); + + it("includes hint when present", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Boolean with Hint", + api_key: "boolean_with_hint", + hint: "This is a hint", + }), + }); + + const config = booleanGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a hint"); + }); + + it("includes default_value when present", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Boolean with Default", + api_key: "boolean_with_default", + default_value: true, + }), + }); + + const config = booleanGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe(true); + }); + + it("handles false default value", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Boolean False Default", + api_key: "boolean_false_default", + default_value: false, + }), + }); + + const config = booleanGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe(false); + }); + + it("handles minimal configuration", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Minimal Boolean", + api_key: "minimal_boolean", + }), + }); + + const config = booleanGenerator.generateBuildConfig(); + expect(config).toEqual({ + label: "Minimal Boolean", + body: { + api_key: "minimal_boolean", + }, + }); + }); + + it("handles field with empty appearance", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Empty Appearance", + api_key: "empty_appearance", + appearance: { addons: [], editor: "checkbox", parameters: {} } as any, + }), + }); + + const config = booleanGenerator.generateBuildConfig(); + expect(config.label).toBe("Empty Appearance"); + expect(config.body?.api_key).toBe("empty_appearance"); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call string", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Test Boolean", + api_key: "test_boolean", + hint: "Test hint", + }), + }); + + const methodCall = booleanGenerator.generateMethodCall(); + expect(methodCall).toContain(".addBoolean("); + expect(methodCall).toContain('label: "Test Boolean"'); + expect(methodCall).toContain('api_key: "test_boolean"'); + expect(methodCall).toContain('hint: "Test hint"'); + }); + + it("generates minimal method call", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Simple", + api_key: "simple", + }), + }); + + const methodCall = booleanGenerator.generateMethodCall(); + expect(methodCall).toContain(".addBoolean("); + expect(methodCall).toContain('label: "Simple"'); + expect(methodCall).toContain('api_key: "simple"'); + expect(methodCall).not.toContain("hint"); + expect(methodCall).not.toContain("validators"); + }); + + it("generates method call with default value", () => { + const booleanGenerator = new BooleanFieldGenerator({ + field: createMockField({ + label: "Default Boolean", + api_key: "default_boolean", + default_value: true, + }), + }); + + const methodCall = booleanGenerator.generateMethodCall(); + expect(methodCall).toContain("default_value: true"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/BooleanFieldGenerator.ts b/src/FileGeneration/FieldGenerators/BooleanFieldGenerator.ts new file mode 100644 index 0000000..e148e8f --- /dev/null +++ b/src/FileGeneration/FieldGenerators/BooleanFieldGenerator.ts @@ -0,0 +1,16 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +/** + * Generates ItemTypeBuilder.addBoolean() method calls. + * Uses base class template methods for standard behavior. + */ +export class BooleanFieldGenerator extends FieldGenerator<"addBoolean"> { + getMethodCallName() { + return "addBoolean" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addBoolean"> { + return this.generateTemplateConfig(); + } +} diff --git a/src/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator.test.ts new file mode 100644 index 0000000..1157ba4 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator.test.ts @@ -0,0 +1,244 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { BooleanRadioGroupConfig } from "@/Fields/BooleanRadioGroup"; +import { BooleanRadioGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "boolean", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: { + positive_radio: { label: "Yes", hint: "Select for true" }, + negative_radio: { label: "No", hint: "Select for false" }, + }, + } as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("BooleanRadioGroupFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a boolean radio group field with label and radio options", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Agree to Terms", + api_key: "agree_to_terms", + }), + }); + + expect(booleanRadioGroupGenerator.generateBuildConfig()).toEqual({ + label: "Agree to Terms", + positive_radio: { label: "Yes", hint: "Select for true" }, + negative_radio: { label: "No", hint: "Select for false" }, + body: { + api_key: "agree_to_terms", + }, + } satisfies BooleanRadioGroupConfig); + }); + + it("returns correct method call name", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(booleanRadioGroupGenerator.getMethodCallName()).toBe( + "addBooleanRadioGroup", + ); + }); + + it("includes hint when present", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Boolean Radio Group with Hint", + api_key: "boolean_radio_group_with_hint", + hint: "This is a field hint", + }), + }); + + const config = booleanRadioGroupGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a field hint"); + }); + + it("includes default_value when present", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Boolean Radio Group with Default", + api_key: "boolean_radio_group_with_default", + default_value: true, + }), + }); + + const config = booleanRadioGroupGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe(true); + }); + + it("handles custom radio labels", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Custom Radio Labels", + api_key: "custom_radio_labels", + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: { + positive_radio: { label: "Enable", hint: "Turn this on" }, + negative_radio: { label: "Disable", hint: "Turn this off" }, + }, + } as any, + }), + }); + + const config = booleanRadioGroupGenerator.generateBuildConfig(); + expect(config.positive_radio).toEqual({ + label: "Enable", + hint: "Turn this on", + }); + expect(config.negative_radio).toEqual({ + label: "Disable", + hint: "Turn this off", + }); + }); + + it("handles radio labels without hints", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "No Hints Radio", + api_key: "no_hints_radio", + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: { + positive_radio: { label: "True" }, + negative_radio: { label: "False" }, + }, + } as any, + }), + }); + + const config = booleanRadioGroupGenerator.generateBuildConfig(); + expect(config.positive_radio).toEqual({ label: "True" }); + expect(config.negative_radio).toEqual({ label: "False" }); + }); + + it("handles missing radio parameters with defaults", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Default Radio Labels", + api_key: "default_radio_labels", + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: {}, + } as any, + }), + }); + + const config = booleanRadioGroupGenerator.generateBuildConfig(); + expect(config.positive_radio).toEqual({ label: "Yes" }); + expect(config.negative_radio).toEqual({ label: "No" }); + }); + + it("handles empty appearance parameters", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Empty Parameters", + api_key: "empty_parameters", + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: undefined, + } as any, + }), + }); + + const config = booleanRadioGroupGenerator.generateBuildConfig(); + expect(config.positive_radio).toEqual({ label: "Yes" }); + expect(config.negative_radio).toEqual({ label: "No" }); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call string", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Test Boolean Radio Group", + api_key: "test_boolean_radio_group", + hint: "Test hint", + }), + }); + + const methodCall = booleanRadioGroupGenerator.generateMethodCall(); + expect(methodCall).toContain(".addBooleanRadioGroup("); + expect(methodCall).toContain('label: "Test Boolean Radio Group"'); + expect(methodCall).toContain('api_key: "test_boolean_radio_group"'); + expect(methodCall).toContain('hint: "Test hint"'); + expect(methodCall).toContain("positive_radio"); + expect(methodCall).toContain("negative_radio"); + }); + + it("generates method call with custom radio labels", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Custom Radio", + api_key: "custom_radio", + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: { + positive_radio: { label: "Approve", hint: "Click to approve" }, + negative_radio: { label: "Reject", hint: "Click to reject" }, + }, + } as any, + }), + }); + + const methodCall = booleanRadioGroupGenerator.generateMethodCall(); + expect(methodCall).toContain('label: "Approve"'); + expect(methodCall).toContain('hint: "Click to approve"'); + expect(methodCall).toContain('label: "Reject"'); + expect(methodCall).toContain('hint: "Click to reject"'); + }); + + it("generates minimal method call", () => { + const booleanRadioGroupGenerator = new BooleanRadioGroupFieldGenerator({ + field: createMockField({ + label: "Simple Radio Group", + api_key: "simple_radio_group", + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: { + positive_radio: { label: "On" }, + negative_radio: { label: "Off" }, + }, + } as any, + }), + }); + + const methodCall = booleanRadioGroupGenerator.generateMethodCall(); + expect(methodCall).toContain(".addBooleanRadioGroup("); + expect(methodCall).toContain('label: "Simple Radio Group"'); + expect(methodCall).toContain('api_key: "simple_radio_group"'); + expect(methodCall).toContain('label: "On"'); + expect(methodCall).toContain('label: "Off"'); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator.ts b/src/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator.ts new file mode 100644 index 0000000..bd2c837 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator.ts @@ -0,0 +1,51 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +export class BooleanRadioGroupFieldGenerator extends FieldGenerator<"addBooleanRadioGroup"> { + getMethodCallName() { + return "addBooleanRadioGroup" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addBooleanRadioGroup"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody(); + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + const { positive_radio, negative_radio } = this.extractRadioLabels(); + + return { + ...config, + positive_radio, + negative_radio, + ...(this.hasBodyContent(body) && { body }), + }; + } + + private extractRadioLabels() { + const parameters = this.extractAppearanceParameters<{ + positive_radio?: { label?: string; hint?: string }; + negative_radio?: { label?: string; hint?: string }; + }>({ + positive_radio: { type: "object" }, + negative_radio: { type: "object" }, + }); + + const positive_radio = { + label: parameters.positive_radio?.label || "Yes", + ...(parameters.positive_radio?.hint && { + hint: parameters.positive_radio.hint, + }), + }; + + const negative_radio = { + label: parameters.negative_radio?.label || "No", + ...(parameters.negative_radio?.hint && { + hint: parameters.negative_radio.hint, + }), + }; + + return { positive_radio, negative_radio }; + } +} diff --git a/src/FileGeneration/FieldGenerators/ColorPickerFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/ColorPickerFieldGenerator.test.ts new file mode 100644 index 0000000..7f30cb4 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/ColorPickerFieldGenerator.test.ts @@ -0,0 +1,412 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { ColorPickerConfig } from "@/Fields/ColorPicker"; +import { ColorPickerFieldGenerator } from "@/FileGeneration/FieldGenerators/ColorPickerFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "color", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "color_picker", + parameters: { + enable_alpha: false, + preset_colors: [], + }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("ColorPickerFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a color picker with label", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Brand Color", + api_key: "brand_color", + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Brand Color", + enable_alpha: false, + preset_colors: [], + body: { + api_key: "brand_color", + }, + } satisfies ColorPickerConfig); + }); + + it("does not include position", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Color without Position", + api_key: "color_without_position", + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Color without Position", + enable_alpha: false, + preset_colors: [], + body: { + api_key: "color_without_position", + }, + } satisfies ColorPickerConfig); + }); + + it("can generate a color picker with a hint", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Color with Hint", + api_key: "color_with_hint", + hint: "Choose the brand primary color", + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Color with Hint", + enable_alpha: false, + preset_colors: [], + body: { + api_key: "color_with_hint", + hint: "Choose the brand primary color", + }, + } satisfies ColorPickerConfig); + }); + + it("can generate a color picker without a hint", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Color without Hint", + api_key: "color_without_hint", + hint: null, + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Color without Hint", + enable_alpha: false, + preset_colors: [], + body: { + api_key: "color_without_hint", + }, + } satisfies ColorPickerConfig); + }); + + it("should not include validators if none are set", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Color without Validators", + api_key: "color_without_validators", + validators: {}, + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Color without Validators", + enable_alpha: false, + preset_colors: [], + body: { + api_key: "color_without_validators", + }, + } satisfies ColorPickerConfig); + }); + }); + + describe("Appearance parameters", () => { + it("can extract enable_alpha parameter", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Color with Alpha", + api_key: "color_with_alpha", + appearance: { + addons: [], + editor: "color_picker", + parameters: { + enable_alpha: true, + preset_colors: [], + }, + }, + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Color with Alpha", + enable_alpha: true, + preset_colors: [], + body: { + api_key: "color_with_alpha", + }, + } satisfies ColorPickerConfig); + }); + + it("can extract preset_colors parameter", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Color with Presets", + api_key: "color_with_presets", + appearance: { + addons: [], + editor: "color_picker", + parameters: { + enable_alpha: false, + preset_colors: ["#FF0000", "#00FF00", "#0000FF"], + }, + }, + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Color with Presets", + enable_alpha: false, + preset_colors: ["#FF0000", "#00FF00", "#0000FF"], + body: { + api_key: "color_with_presets", + }, + } satisfies ColorPickerConfig); + }); + + it("can handle both enable_alpha and preset_colors", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Full Color Picker", + api_key: "full_color_picker", + appearance: { + addons: [], + editor: "color_picker", + parameters: { + enable_alpha: true, + preset_colors: ["#FF5733", "#33FF57", "#3357FF"], + }, + }, + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Full Color Picker", + enable_alpha: true, + preset_colors: ["#FF5733", "#33FF57", "#3357FF"], + body: { + api_key: "full_color_picker", + }, + } satisfies ColorPickerConfig); + }); + + it("handles missing appearance parameters gracefully", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Color No Params", + api_key: "color_no_params", + appearance: { + addons: [], + editor: "color_picker", + parameters: {}, + }, + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Color No Params", + body: { + api_key: "color_no_params", + }, + } satisfies ColorPickerConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required color picker field", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Required Color", + api_key: "required-color-api-key", + validators: { required: {} }, + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Required Color", + enable_alpha: false, + preset_colors: [], + body: { + api_key: "required-color-api-key", + validators: { required: true }, + }, + } satisfies ColorPickerConfig); + }); + + it("can generate a non-required color picker field", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Non-Required Color", + api_key: "non-required-color-api-key", + }), + }); + + expect(colorPickerGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Color", + enable_alpha: false, + preset_colors: [], + body: { + api_key: "non-required-color-api-key", + }, + } satisfies ColorPickerConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Test Color", + api_key: "test_color", + }), + }); + + const methodCall = colorPickerGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addColorPicker\(/); + expect(methodCall).toContain('"Test Color"'); + expect(methodCall).toContain('"test_color"'); + }); + + it("generates method call with enable_alpha", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Alpha Color", + api_key: "alpha_color", + appearance: { + addons: [], + editor: "color_picker", + parameters: { + enable_alpha: true, + preset_colors: [], + }, + }, + }), + }); + + const methodCall = colorPickerGenerator.generateMethodCall(); + + expect(methodCall).toContain("enable_alpha: true"); + expect(methodCall).toMatch(/\.addColorPicker\(/); + }); + + it("generates method call with preset colors", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Preset Color", + api_key: "preset_color", + appearance: { + addons: [], + editor: "color_picker", + parameters: { + enable_alpha: false, + preset_colors: ["#AABBCC", "#DDEEFF"], + }, + }, + }), + }); + + const methodCall = colorPickerGenerator.generateMethodCall(); + + expect(methodCall).toContain('"#AABBCC"'); + expect(methodCall).toContain('"#DDEEFF"'); + expect(methodCall).toMatch(/\.addColorPicker\(/); + }); + + it("generates method call with required validator", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Required Color", + api_key: "required_color", + validators: { required: {} }, + }), + }); + + const methodCall = colorPickerGenerator.generateMethodCall(); + + expect(methodCall).toContain("required: true"); + expect(methodCall).toMatch(/\.addColorPicker\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic Color Picker field from DatoCMS", () => { + const apiResponseField: Field = { + id: "color-field-123", + type: "field", + label: "Primary Color", + field_type: "color", + api_key: "primary_color", + hint: "Choose the primary brand color", + localized: false, + validators: { + required: {}, + }, + position: 1, + appearance: { + addons: [], + editor: "color_picker", + parameters: { + enable_alpha: true, + preset_colors: ["#FF0000", "#00FF00", "#0000FF"], + }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: apiResponseField, + }); + + const config = colorPickerGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Primary Color", + enable_alpha: true, + preset_colors: ["#FF0000", "#00FF00", "#0000FF"], + body: { + api_key: "primary_color", + hint: "Choose the primary brand color", + validators: { + required: true, + }, + }, + } satisfies ColorPickerConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const colorPickerGenerator = new ColorPickerFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(colorPickerGenerator.getMethodCallName()).toBe("addColorPicker"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/ColorPickerFieldGenerator.ts b/src/FileGeneration/FieldGenerators/ColorPickerFieldGenerator.ts new file mode 100644 index 0000000..22bd8e9 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/ColorPickerFieldGenerator.ts @@ -0,0 +1,39 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addColorPicker() method calls. + */ +export class ColorPickerFieldGenerator extends FieldGenerator<"addColorPicker"> { + getMethodCallName() { + return "addColorPicker" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addColorPicker"> { + const config = this.generateTemplateConfig(); + + // Extract appearance parameters using new template method + const enable_alpha = this.extractAppearanceParameter( + "enable_alpha", + "boolean", + ); + const preset_colors = this.extractAppearanceParameter( + "preset_colors", + "array", + ); + + if (enable_alpha !== undefined) { + config.enable_alpha = enable_alpha; + } + + if (preset_colors !== undefined) { + config.preset_colors = preset_colors; + } + + return config; + } + + protected override addFieldSpecificValidators(_validators: any): void { + // ColorPicker only uses standard validators, no custom ones + } +} diff --git a/src/FileGeneration/FieldGenerators/DateFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/DateFieldGenerator.test.ts new file mode 100644 index 0000000..3c03f31 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/DateFieldGenerator.test.ts @@ -0,0 +1,355 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { DateConfig } from "@/Fields/Date"; +import { DateFieldGenerator } from "@/FileGeneration/FieldGenerators/DateFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "date", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "date_picker", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("DateFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a date with label", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date", + api_key: "date", + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Date", + body: { + api_key: "date", + }, + } satisfies DateConfig); + }); + + it("does not include position", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date without Position", + api_key: "date_without_position", + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Date without Position", + body: { + api_key: "date_without_position", + }, + } satisfies DateConfig); + }); + + it("can generate a date with a hint", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date with Hint", + api_key: "date_with_hint", + hint: "This is a test hint", + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Date with Hint", + body: { + api_key: "date_with_hint", + hint: "This is a test hint", + }, + } satisfies DateConfig); + }); + + it("can generate a date without a hint", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date without Hint", + api_key: "date_without_hint", + hint: null, + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Date without Hint", + body: { + api_key: "date_without_hint", + }, + } satisfies DateConfig); + }); + + it("can generate a date with a default value", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date with Default Value", + api_key: "date_with_default_value", + default_value: "2025-07-24", + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Date with Default Value", + body: { + api_key: "date_with_default_value", + default_value: "2025-07-24", + }, + } satisfies DateConfig); + }); + + it("can generate a date without a default value", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date without Default Value", + api_key: "date_without_default_value", + default_value: null, + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Date without Default Value", + body: { + api_key: "date_without_default_value", + }, + } satisfies DateConfig); + }); + + it("should not include validators if none are set", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date without Validators", + api_key: "date_without_validators", + validators: {}, + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Date without Validators", + body: { + api_key: "date_without_validators", + }, + } satisfies DateConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required date field", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Required Date", + api_key: "required-date-api-key", + validators: { required: {} }, + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Required Date", + body: { + api_key: "required-date-api-key", + validators: { required: true }, + }, + } satisfies DateConfig); + }); + + it("can generate a non-required date field", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Non-Required Date", + api_key: "non-required-date-api-key", + }), + }); + + expect(dateGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Date", + body: { + api_key: "non-required-date-api-key", + }, + } satisfies DateConfig); + }); + }); + + describe("Date Range", () => { + it("can generate a date field with a range", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date with Range", + api_key: "date-with-range-api-key", + validators: { + date_range: { min: "2020-01-01", max: "2025-12-31" }, + }, + }), + }); + + const config = dateGenerator.generateBuildConfig(); + + expect(config.label).toBe("Date with Range"); + expect(config.body?.api_key).toBe("date-with-range-api-key"); + expect(config.body?.validators?.date_range?.min).toBeInstanceOf(Date); + expect(config.body?.validators?.date_range?.max).toBeInstanceOf(Date); + expect(config.body?.validators?.date_range?.min).toEqual( + new Date("2020-01-01"), + ); + expect(config.body?.validators?.date_range?.max).toEqual( + new Date("2025-12-31"), + ); + }); + + it("generates method call with new Date() constructor calls", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date with Range", + api_key: "date-with-range-api-key", + validators: { + date_range: { min: "2020-01-01", max: "2025-12-31" }, + }, + }), + }); + + const methodCall = dateGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2020-01-01")'); + expect(methodCall).toContain('new Date("2025-12-31")'); + expect(methodCall).toMatch(/\.addDate\(/); + }); + + it("can generate a date field with a minimum date", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date with Min", + api_key: "date-with-min-api-key", + validators: { date_range: { min: "2020-01-01" } }, + }), + }); + + const config = dateGenerator.generateBuildConfig(); + + expect(config.label).toBe("Date with Min"); + expect(config.body?.api_key).toBe("date-with-min-api-key"); + expect(config.body?.validators?.date_range?.min).toBeInstanceOf(Date); + expect(config.body?.validators?.date_range?.min).toEqual( + new Date("2020-01-01"), + ); + }); + + it("can generate a date field with a maximum date", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date with Max", + api_key: "date-with-max-api-key", + validators: { date_range: { max: "2025-12-31" } }, + }), + }); + + const config = dateGenerator.generateBuildConfig(); + + expect(config.label).toBe("Date with Max"); + expect(config.body?.api_key).toBe("date-with-max-api-key"); + expect(config.body?.validators?.date_range?.max).toBeInstanceOf(Date); + expect(config.body?.validators?.date_range?.max).toEqual( + new Date("2025-12-31"), + ); + }); + + it("preserves original format for date-only strings", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Date Format Test", + api_key: "date-format-test", + validators: { + date_range: { min: "2020-01-01", max: "2025-12-31" }, + }, + }), + }); + + const methodCall = dateGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2020-01-01")'); + expect(methodCall).toContain('new Date("2025-12-31")'); + expect(methodCall).not.toContain("T00:00:00.000Z"); + }); + + it("preserves ISO format for ISO string dates", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "ISO Date Test", + api_key: "iso-date-test", + validators: { + date_range: { + min: "2020-01-01T09:30:00.000Z", + max: "2025-12-31T23:59:59.999Z", + }, + }, + }), + }); + + const methodCall = dateGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2020-01-01T09:30:00.000Z")'); + expect(methodCall).toContain('new Date("2025-12-31T23:59:59.999Z")'); + }); + + it("handles mixed date-only and ISO formats", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Mixed Format Test", + api_key: "mixed-format-test", + validators: { + date_range: { + min: "2020-01-01", + max: "2025-12-31T23:59:59.999Z", + }, + }, + }), + }); + + const methodCall = dateGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2020-01-01")'); + expect(methodCall).toContain('new Date("2025-12-31T23:59:59.999Z")'); + }); + + it("handles edge case date formats", () => { + const dateGenerator = new DateFieldGenerator({ + field: createMockField({ + label: "Edge Case Dates", + api_key: "edge-case-dates", + validators: { + date_range: { + min: "1970-01-01", + max: "9999-12-31", + }, + }, + }), + }); + + const config = dateGenerator.generateBuildConfig(); + + expect(config.body?.validators?.date_range?.min).toBeInstanceOf(Date); + expect(config.body?.validators?.date_range?.max).toBeInstanceOf(Date); + expect( + config.body?.validators?.date_range?.min + ?.toISOString() + .startsWith("1970"), + ).toBe(true); + expect(config.body?.validators?.date_range?.max?.getFullYear()).toBe( + 9999, + ); + }); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/DateFieldGenerator.ts b/src/FileGeneration/FieldGenerators/DateFieldGenerator.ts new file mode 100644 index 0000000..83d598b --- /dev/null +++ b/src/FileGeneration/FieldGenerators/DateFieldGenerator.ts @@ -0,0 +1,72 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addDate() method calls. + */ +export class DateFieldGenerator extends FieldGenerator<"addDate"> { + private static readonly DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/; + + getMethodCallName() { + return "addDate" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addDate"> { + const config = this.createBaseConfig() as MethodNameToConfig<"addDate">; + const body = this.buildDateFieldBody(); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildDateFieldBody(): NonNullable< + MethodNameToConfig<"addDate">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addDate">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addDateValidators(body); + + return body; + } + + private addDateValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + this.processRequiredValidator(validators); + this.processDateRangeValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + private processDateRangeValidator( + validators: NonNullable< + NonNullable["body"]>["validators"] + >, + ): void { + // Use base class generic range validator with date-only string preservation logic + this.processRangeValidator(validators, "date_range", (dateString: string) => + this.isDateOnlyString(dateString), + ); + } + + private isDateOnlyString(dateString: string): boolean { + return DateFieldGenerator.DATE_ONLY_PATTERN.test(dateString); + } +} diff --git a/src/FileGeneration/FieldGenerators/DateTimeFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/DateTimeFieldGenerator.test.ts new file mode 100644 index 0000000..129665b --- /dev/null +++ b/src/FileGeneration/FieldGenerators/DateTimeFieldGenerator.test.ts @@ -0,0 +1,460 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { DateTimeConfig } from "@/Fields/DateTime"; +import { DateTimeFieldGenerator } from "@/FileGeneration/FieldGenerators/DateTimeFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "date_time", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "date_time_picker", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("DateTimeFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a datetime with label", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "Date time", + api_key: "date_time", + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "Date time", + body: { + api_key: "date_time", + }, + } satisfies DateTimeConfig); + }); + + it("does not include position", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime without Position", + api_key: "datetime_without_position", + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "DateTime without Position", + body: { + api_key: "datetime_without_position", + }, + } satisfies DateTimeConfig); + }); + + it("can generate a datetime with a hint", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "Date time", + api_key: "date_time", + hint: "test hint", + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "Date time", + body: { + api_key: "date_time", + hint: "test hint", + }, + } satisfies DateTimeConfig); + }); + + it("can generate a datetime without a hint", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime without Hint", + api_key: "datetime_without_hint", + hint: null, + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "DateTime without Hint", + body: { + api_key: "datetime_without_hint", + }, + } satisfies DateTimeConfig); + }); + + it("can generate a datetime with a default value", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime with Default Value", + api_key: "datetime_with_default_value", + default_value: "2025-07-10T05:00:00+01:00", + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "DateTime with Default Value", + body: { + api_key: "datetime_with_default_value", + default_value: "2025-07-10T05:00:00+01:00", + }, + } satisfies DateTimeConfig); + }); + + it("can generate a datetime without a default value", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime without Default Value", + api_key: "datetime_without_default_value", + default_value: null, + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "DateTime without Default Value", + body: { + api_key: "datetime_without_default_value", + }, + } satisfies DateTimeConfig); + }); + + it("should not include validators if none are set", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime without Validators", + api_key: "datetime_without_validators", + validators: {}, + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "DateTime without Validators", + body: { + api_key: "datetime_without_validators", + }, + } satisfies DateTimeConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required datetime field", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "Required DateTime", + api_key: "required-datetime-api-key", + validators: { required: {} }, + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "Required DateTime", + body: { + api_key: "required-datetime-api-key", + validators: { required: true }, + }, + } satisfies DateTimeConfig); + }); + + it("can generate a non-required datetime field", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "Non-Required DateTime", + api_key: "non-required-datetime-api-key", + }), + }); + + expect(dateTimeGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required DateTime", + body: { + api_key: "non-required-datetime-api-key", + }, + } satisfies DateTimeConfig); + }); + }); + + describe("DateTime Range", () => { + it("can generate a datetime field with a range", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime with Range", + api_key: "datetime-with-range-api-key", + validators: { + date_time_range: { + min: "2025-07-03T05:00:00+01:00", + max: "2025-07-12T05:00:00+01:00", + }, + }, + }), + }); + + const config = dateTimeGenerator.generateBuildConfig(); + + expect(config.label).toBe("DateTime with Range"); + expect(config.body?.api_key).toBe("datetime-with-range-api-key"); + expect(config.body?.validators?.date_time_range?.min).toBeInstanceOf( + Date, + ); + expect(config.body?.validators?.date_time_range?.max).toBeInstanceOf( + Date, + ); + expect(config.body?.validators?.date_time_range?.min).toEqual( + new Date("2025-07-03T05:00:00+01:00"), + ); + expect(config.body?.validators?.date_time_range?.max).toEqual( + new Date("2025-07-12T05:00:00+01:00"), + ); + }); + + it("generates method call with new Date() constructor calls", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime with Range", + api_key: "datetime-with-range-api-key", + validators: { + date_time_range: { + min: "2025-07-03T05:00:00+01:00", + max: "2025-07-12T05:00:00+01:00", + }, + }, + }), + }); + + const methodCall = dateTimeGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2025-07-03T05:00:00+01:00")'); + expect(methodCall).toContain('new Date("2025-07-12T05:00:00+01:00")'); + expect(methodCall).toMatch(/\.addDateTime\(/); + }); + + it("can generate a datetime field with a minimum datetime", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime with Min", + api_key: "datetime-with-min-api-key", + validators: { + date_time_range: { min: "2025-07-03T05:00:00+01:00" }, + }, + }), + }); + + const config = dateTimeGenerator.generateBuildConfig(); + + expect(config.label).toBe("DateTime with Min"); + expect(config.body?.api_key).toBe("datetime-with-min-api-key"); + expect(config.body?.validators?.date_time_range?.min).toBeInstanceOf( + Date, + ); + expect(config.body?.validators?.date_time_range?.min).toEqual( + new Date("2025-07-03T05:00:00+01:00"), + ); + }); + + it("can generate a datetime field with a maximum datetime", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime with Max", + api_key: "datetime-with-max-api-key", + validators: { + date_time_range: { max: "2025-07-12T05:00:00+01:00" }, + }, + }), + }); + + const config = dateTimeGenerator.generateBuildConfig(); + + expect(config.label).toBe("DateTime with Max"); + expect(config.body?.api_key).toBe("datetime-with-max-api-key"); + expect(config.body?.validators?.date_time_range?.max).toBeInstanceOf( + Date, + ); + expect(config.body?.validators?.date_time_range?.max).toEqual( + new Date("2025-07-12T05:00:00+01:00"), + ); + }); + + it("preserves original format for timezone-aware datetime strings", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "DateTime Format Test", + api_key: "datetime-format-test", + validators: { + date_time_range: { + min: "2025-07-03T05:00:00+01:00", + max: "2025-07-12T05:00:00+01:00", + }, + }, + }), + }); + + const methodCall = dateTimeGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2025-07-03T05:00:00+01:00")'); + expect(methodCall).toContain('new Date("2025-07-12T05:00:00+01:00")'); + }); + + it("preserves UTC format for UTC datetime strings", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "UTC DateTime Test", + api_key: "utc-datetime-test", + validators: { + date_time_range: { + min: "2025-07-03T04:00:00.000Z", + max: "2025-07-12T04:00:00.000Z", + }, + }, + }), + }); + + const methodCall = dateTimeGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2025-07-03T04:00:00.000Z")'); + expect(methodCall).toContain('new Date("2025-07-12T04:00:00.000Z")'); + }); + + it("handles mixed timezone formats", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "Mixed Timezone Test", + api_key: "mixed-timezone-test", + validators: { + date_time_range: { + min: "2025-07-03T05:00:00+01:00", + max: "2025-07-12T04:00:00.000Z", + }, + }, + }), + }); + + const methodCall = dateTimeGenerator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2025-07-03T05:00:00+01:00")'); + expect(methodCall).toContain('new Date("2025-07-12T04:00:00.000Z")'); + }); + + it("handles edge case datetime formats", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "Edge Case Date Times", + api_key: "edge-case-date-times", + validators: { + date_time_range: { + min: "1970-01-01T00:00:00.000Z", + max: "2099-12-31T23:59:59.999Z", + }, + }, + }), + }); + + const config = dateTimeGenerator.generateBuildConfig(); + + expect(config.body?.validators?.date_time_range?.min).toBeInstanceOf( + Date, + ); + expect(config.body?.validators?.date_time_range?.max).toBeInstanceOf( + Date, + ); + expect( + (config.body?.validators?.date_time_range?.min as Date) + ?.toISOString() + .startsWith("1970"), + ).toBe(true); + expect( + ( + config.body?.validators?.date_time_range?.max as Date + )?.getFullYear(), + ).toBe(2099); + }); + }); + + describe("Combined validators", () => { + it("can generate a datetime field with both required and range validators", () => { + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: createMockField({ + label: "Required DateTime with Range", + api_key: "required-datetime-with-range", + validators: { + required: {}, + date_time_range: { + min: "2025-07-03T05:00:00+01:00", + max: "2025-07-12T05:00:00+01:00", + }, + }, + }), + }); + + const config = dateTimeGenerator.generateBuildConfig(); + + expect(config.label).toBe("Required DateTime with Range"); + expect(config.body?.api_key).toBe("required-datetime-with-range"); + expect(config.body?.validators?.required).toBe(true); + expect(config.body?.validators?.date_time_range?.min).toBeInstanceOf( + Date, + ); + expect(config.body?.validators?.date_time_range?.max).toBeInstanceOf( + Date, + ); + }); + }); + }); + + describe("Real-world API response test", () => { + it("can handle the exact API response from DatoCMS", () => { + const apiResponseField: Field = { + id: "field-id-123", + type: "field", + label: "Date time", + field_type: "date_time", + api_key: "date_time", + hint: "test hint", + localized: false, + validators: { + required: {}, + date_time_range: { + min: "2025-07-03T05:00:00+01:00", + max: "2025-07-12T05:00:00+01:00", + }, + }, + position: 2, + appearance: { addons: [], editor: "date_time_picker", parameters: {} }, + default_value: "2025-07-10T05:00:00+01:00", + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const dateTimeGenerator = new DateTimeFieldGenerator({ + field: apiResponseField, + }); + + const config = dateTimeGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Date time", + body: { + api_key: "date_time", + hint: "test hint", + default_value: "2025-07-10T05:00:00+01:00", + validators: { + required: true, + date_time_range: { + min: new Date("2025-07-03T05:00:00+01:00"), + max: new Date("2025-07-12T05:00:00+01:00"), + }, + }, + }, + } satisfies DateTimeConfig); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/DateTimeFieldGenerator.ts b/src/FileGeneration/FieldGenerators/DateTimeFieldGenerator.ts new file mode 100644 index 0000000..cbcaa99 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/DateTimeFieldGenerator.ts @@ -0,0 +1,64 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addDateTime() method calls. + */ +export class DateTimeFieldGenerator extends FieldGenerator<"addDateTime"> { + getMethodCallName() { + return "addDateTime" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addDateTime"> { + const config = this.createBaseConfig() as MethodNameToConfig<"addDateTime">; + const body = this.buildDateTimeFieldBody(); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildDateTimeFieldBody(): NonNullable< + MethodNameToConfig<"addDateTime">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addDateTime">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addDateTimeValidators(body); + + return body; + } + + private addDateTimeValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + this.processRequiredValidator(validators); + this.processDateTimeRangeValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + private processDateTimeRangeValidator( + validators: NonNullable< + NonNullable["body"]>["validators"] + >, + ): void { + // Use base class generic range validator - always preserve strings for datetime fields + this.processRangeValidator(validators, "date_time_range", true); + } +} diff --git a/src/FileGeneration/FieldGenerators/EmailFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/EmailFieldGenerator.test.ts new file mode 100644 index 0000000..644a245 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/EmailFieldGenerator.test.ts @@ -0,0 +1,137 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { EmailFieldGenerator } from "@/FileGeneration/FieldGenerators/EmailFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "string", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "test-item-type", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("EmailFieldGenerator", () => { + let emailGenerator: EmailFieldGenerator; + + beforeEach(() => { + emailGenerator = new EmailFieldGenerator({ + field: createMockField({ + label: "Test Email", + api_key: "test_email", + }), + }); + }); + + describe("getMethodCallName", () => { + it("returns correct method call name", () => { + expect(emailGenerator.getMethodCallName()).toBe("addEmail"); + }); + }); + + describe("generateBuildConfig", () => { + it("generates basic config without body when no additional properties", () => { + const config = emailGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Test Email", + body: { + api_key: "test_email", + }, + }); + }); + + it("includes hint when present", () => { + const emailGenerator = new EmailFieldGenerator({ + field: createMockField({ + label: "Email with Hint", + api_key: "email_with_hint", + hint: "This is a field hint", + }), + }); + + const config = emailGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a field hint"); + }); + + it("includes default value when present", () => { + const emailGenerator = new EmailFieldGenerator({ + field: createMockField({ + label: "Email with Default", + api_key: "email_with_default", + default_value: "test@example.com", + }), + }); + + const config = emailGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("test@example.com"); + }); + + it("includes validators when present", () => { + const emailGenerator = new EmailFieldGenerator({ + field: createMockField({ + label: "Required Email", + api_key: "required_email", + validators: { + required: true, + format: { + predefined_pattern: "email", + }, + } as any, + }), + }); + + const config = emailGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + expect(config.body?.validators?.format).toEqual({ + predefined_pattern: "email", + }); + }); + }); + + describe("generateMethodCall", () => { + it("generates basic method call", () => { + const methodCall = emailGenerator.generateMethodCall(); + expect(methodCall).toContain(".addEmail("); + expect(methodCall).toContain('label: "Test Email"'); + }); + + it("generates method call with body properties", () => { + const emailGenerator = new EmailFieldGenerator({ + field: createMockField({ + label: "Complex Email", + api_key: "complex_email", + hint: "Enter your email address", + default_value: "user@example.com", + validators: { + required: true, + format: { + predefined_pattern: "email", + }, + } as any, + }), + }); + + const methodCall = emailGenerator.generateMethodCall(); + expect(methodCall).toContain(".addEmail("); + expect(methodCall).toContain('label: "Complex Email"'); + expect(methodCall).toContain('api_key: "complex_email"'); + expect(methodCall).toContain('hint: "Enter your email address"'); + expect(methodCall).toContain('default_value: "user@example.com"'); + expect(methodCall).toContain("required: true"); + expect(methodCall).toContain("format:"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/EmailFieldGenerator.ts b/src/FileGeneration/FieldGenerators/EmailFieldGenerator.ts new file mode 100644 index 0000000..e4ccb38 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/EmailFieldGenerator.ts @@ -0,0 +1,16 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addEmail() method calls. + * Uses base class template methods for standard behavior. + */ +export class EmailFieldGenerator extends FieldGenerator<"addEmail"> { + getMethodCallName() { + return "addEmail" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addEmail"> { + return this.generateTemplateConfig(); + } +} diff --git a/src/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator.test.ts new file mode 100644 index 0000000..6dc2b1e --- /dev/null +++ b/src/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator.test.ts @@ -0,0 +1,263 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { ExternalVideoConfig } from "@/Fields/ExternalVideo"; +import { ExternalVideoFieldGenerator } from "@/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "video", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "video", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("ExternalVideoFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate an external video field with label", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Promotional Video", + api_key: "promotional_video", + }), + }); + + expect(externalVideoGenerator.generateBuildConfig()).toEqual({ + label: "Promotional Video", + body: { + api_key: "promotional_video", + }, + } satisfies ExternalVideoConfig); + }); + + it("does not include position", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Video without Position", + api_key: "video_without_position", + }), + }); + + expect(externalVideoGenerator.generateBuildConfig()).toEqual({ + label: "Video without Position", + body: { + api_key: "video_without_position", + }, + } satisfies ExternalVideoConfig); + }); + + it("can generate an external video field with a hint", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Video with Hint", + api_key: "video_with_hint", + hint: "Upload a video", + }), + }); + + expect(externalVideoGenerator.generateBuildConfig()).toEqual({ + label: "Video with Hint", + body: { + api_key: "video_with_hint", + hint: "Upload a video", + }, + } satisfies ExternalVideoConfig); + }); + + it("can generate an external video field without a hint", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Video without Hint", + api_key: "video_without_hint", + hint: null, + }), + }); + + expect(externalVideoGenerator.generateBuildConfig()).toEqual({ + label: "Video without Hint", + body: { + api_key: "video_without_hint", + }, + } satisfies ExternalVideoConfig); + }); + + it("should not include validators if none are set", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Video without Validators", + api_key: "video_without_validators", + validators: {}, + }), + }); + + expect(externalVideoGenerator.generateBuildConfig()).toEqual({ + label: "Video without Validators", + body: { + api_key: "video_without_validators", + }, + } satisfies ExternalVideoConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required external video field", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Required Video", + api_key: "required_video", + validators: { required: {} }, + }), + }); + + expect(externalVideoGenerator.generateBuildConfig()).toEqual({ + label: "Required Video", + body: { + api_key: "required_video", + validators: { required: true }, + }, + } satisfies ExternalVideoConfig); + }); + + it("can generate a non-required external video field", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Non-Required Video", + api_key: "non_required_video", + }), + }); + + expect(externalVideoGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Video", + body: { + api_key: "non_required_video", + }, + } satisfies ExternalVideoConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Test Video", + api_key: "test_video", + }), + }); + + const methodCall = externalVideoGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addExternalVideo\(/); + expect(methodCall).toContain('"Test Video"'); + expect(methodCall).toContain('"test_video"'); + }); + + it("generates method call with validators", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Video with Validators", + api_key: "video_with_validators", + validators: { + required: {}, + }, + }), + }); + + const methodCall = externalVideoGenerator.generateMethodCall(); + + expect(methodCall).toContain("required: true"); + expect(methodCall).toMatch(/\.addExternalVideo\(/); + }); + + it("generates method call with hint", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Video with Hint", + api_key: "video_with_hint", + hint: "Upload promotional video", + }), + }); + + const methodCall = externalVideoGenerator.generateMethodCall(); + + expect(methodCall).toContain('"Upload promotional video"'); + expect(methodCall).toMatch(/\.addExternalVideo\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic External Video field from DatoCMS", () => { + const apiResponseField: Field = { + id: "video-field-123", + type: "field", + label: "Hero Video", + field_type: "video", + api_key: "hero_video", + hint: "Upload hero section video", + localized: false, + validators: { + required: {}, + }, + position: 1, + appearance: { + addons: [], + editor: "video", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: apiResponseField, + }); + + const config = externalVideoGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Hero Video", + body: { + api_key: "hero_video", + hint: "Upload hero section video", + validators: { + required: true, + }, + }, + } satisfies ExternalVideoConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const externalVideoGenerator = new ExternalVideoFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(externalVideoGenerator.getMethodCallName()).toBe( + "addExternalVideo", + ); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator.ts b/src/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator.ts new file mode 100644 index 0000000..0deb28e --- /dev/null +++ b/src/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator.ts @@ -0,0 +1,55 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addExternalVideo() method calls. + */ +export class ExternalVideoFieldGenerator extends FieldGenerator<"addExternalVideo"> { + getMethodCallName() { + return "addExternalVideo" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addExternalVideo"> { + const config = + this.createBaseConfig() as MethodNameToConfig<"addExternalVideo">; + const body = this.buildExternalVideoFieldBody(); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildExternalVideoFieldBody(): NonNullable< + MethodNameToConfig<"addExternalVideo">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addExternalVideo">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addExternalVideoValidators(body); + + return body; + } + + private addExternalVideoValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + this.processRequiredValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/FieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/FieldGenerator.test.ts new file mode 100644 index 0000000..e006687 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/FieldGenerator.test.ts @@ -0,0 +1,685 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { + FieldGenerator, + type FieldGeneratorConfig, +} from "@/FileGeneration/FieldGenerators/FieldGenerator"; + +function createMockItemType( + name: string, + modular_block: boolean = false, +): ItemType { + return { + id: `test-${name.toLowerCase()}-id`, + type: "item_type", + name, + api_key: name.toLowerCase().replace(/\s+/g, "_"), + collection_appearance: "table", + singleton: false, + all_locales_required: true, + sortable: false, + modular_block, + draft_mode_active: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_meta: null, + has_singleton_item: false, + hint: null, + inverse_relationships_enabled: false, + singleton_item: null, + fields: [], + fieldsets: [], + presentation_title_field: null, + presentation_image_field: null, + title_field: null, + image_preview_field: null, + excerpt_field: null, + ordering_field: null, + workflow: null, + meta: { has_singleton_item: false }, + } as ItemType; +} + +// Mock concrete implementation for testing +class MockDateGenerator extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + generateBuildConfig() { + return { + label: this.field.label, + body: { + position: this.field.position, + }, + } as any; + } +} + +describe("FieldGenerator", () => { + let mockField: Field; + let config: FieldGeneratorConfig; + + beforeEach(() => { + mockField = { + id: "test-id", + type: "field", + label: "Test Field", + field_type: "date", + api_key: "test-date-api-key", + hint: "test hint", + localized: false, + validators: {}, + position: 1, + appearance: { addons: [], editor: "date_picker", parameters: {} }, + default_value: "2025-07-24", + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + config = { + field: mockField, + }; + }); + + describe("generateMethodCall", () => { + it("should generate correct method call string for date field", () => { + const generator = new MockDateGenerator(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toBe( + `.addDate({ label: "Test Field", body: { position: 1 } })`, + ); + }); + + it("should serialize Date objects with ISO strings", () => { + class MockDateGeneratorWithISO extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + return { + label: this.field.label, + body: { + date_value: new Date("2025-07-14T10:30:00.000Z"), + }, + }; + } + } + + const generator = new MockDateGeneratorWithISO(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2025-07-14T10:30:00.000Z")'); + }); + + it("should serialize Date objects with original string format when available", () => { + class MockDateGeneratorWithOriginal extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const date = new Date("2025-07-14") as Date & { + _originalString?: string; + }; + date._originalString = "2025-07-14"; + + return { + label: this.field.label, + body: { + date_value: date, + }, + }; + } + } + + const generator = new MockDateGeneratorWithOriginal(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2025-07-14")'); + expect(methodCall).not.toContain("T00:00:00.000Z"); + }); + + it("should handle nested objects with Date values", () => { + class MockDateGeneratorWithNested extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const minDate = new Date("2020-01-01") as Date & { + _originalString?: string; + }; + minDate._originalString = "2020-01-01"; + + const maxDate = new Date("2025-12-31") as Date & { + _originalString?: string; + }; + maxDate._originalString = "2025-12-31"; + + return { + label: this.field.label, + body: { + validators: { + date_range: { + min: minDate, + max: maxDate, + }, + }, + }, + }; + } + } + + const generator = new MockDateGeneratorWithNested(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2020-01-01")'); + expect(methodCall).toContain('new Date("2025-12-31")'); + expect(methodCall).not.toContain("T00:00:00.000Z"); + }); + + it("should handle null and undefined values", () => { + class MockGeneratorWithNulls extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + return { + label: this.field.label, + body: { + null_value: null, + undefined_value: undefined, + api_key: this.field.api_key, + }, + }; + } + } + + const generator = new MockGeneratorWithNulls(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain("null_value: null"); + expect(methodCall).toContain("undefined_value: undefined"); + }); + + it("should handle arrays of different types", () => { + class MockGeneratorWithArrays extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const date1 = new Date("2020-01-01") as Date & { + _originalString?: string; + }; + date1._originalString = "2020-01-01"; + + const date2 = new Date("2025-12-31T23:59:59.999Z"); + + return { + label: this.field.label, + body: { + string_array: ["one", "two", "three"], + number_array: [1, 2, 3, 42], + date_array: [date1, date2], + mixed_array: ["text", 123, true, null], + }, + }; + } + } + + const generator = new MockGeneratorWithArrays(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('string_array: ["one", "two", "three"]'); + expect(methodCall).toContain("number_array: [1, 2, 3, 42]"); + expect(methodCall).toContain('new Date("2020-01-01")'); + expect(methodCall).toContain('new Date("2025-12-31T23:59:59.999Z")'); + expect(methodCall).toContain('mixed_array: ["text", 123, true, null]'); + }); + + it("should handle special characters in strings", () => { + class MockGeneratorWithSpecialChars extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + return { + label: 'Special "quotes" and \\backslashes', + body: { + emoji_text: "Hello πŸ‘‹ World 🌍", + newline_text: "Line 1\nLine 2\nLine 3", + // cspell:disable-next-line + unicode_text: "Unicode: àÑÒãÀΓ₯æç", + json_like: '{"nested": "value"}', + }, + }; + } + } + + const generator = new MockGeneratorWithSpecialChars(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('Special \\"quotes\\" and \\\\backslashes'); + expect(methodCall).toContain("Hello πŸ‘‹ World 🌍"); + expect(methodCall).toContain("Line 1\\nLine 2\\nLine 3"); + // cspell:disable-next-line + expect(methodCall).toContain("Unicode: àÑÒãÀΓ₯æç"); + }); + + it("should handle deeply nested objects", () => { + class MockGeneratorWithDeepNesting extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const date = new Date("2025-01-01") as Date & { + _originalString?: string; + }; + date._originalString = "2025-01-01"; + + return { + label: this.field.label, + body: { + level1: { + level2: { + level3: { + deep_date: date, + deep_array: [1, 2, { nested: "value" }], + deep_string: "deeply nested", + }, + }, + }, + }, + }; + } + } + + const generator = new MockGeneratorWithDeepNesting(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('new Date("2025-01-01")'); + expect(methodCall).toContain('deep_array: [1, 2, { nested: "value" }]'); + expect(methodCall).toContain('deep_string: "deeply nested"'); + }); + + it("should handle boolean values correctly", () => { + class MockGeneratorWithBooleans extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + return { + label: this.field.label, + body: { + true_value: true, + false_value: false, + zero_number: 0, + empty_string: "", + }, + }; + } + } + + const generator = new MockGeneratorWithBooleans(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain("true_value: true"); + expect(methodCall).toContain("false_value: false"); + expect(methodCall).toContain("zero_number: 0"); + expect(methodCall).toContain('empty_string: ""'); + }); + + it("should handle empty objects and arrays", () => { + class MockGeneratorWithEmpty extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + return { + label: this.field.label, + body: { + empty_object: {}, + empty_array: [], + nested_empty: { + empty_inside: {}, + array_inside: [], + }, + }, + }; + } + } + + const generator = new MockGeneratorWithEmpty(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain("empty_object: {}"); + expect(methodCall).toContain("empty_array: []"); + expect(methodCall).toContain("empty_inside: {}"); + expect(methodCall).toContain("array_inside: []"); + }); + + it("should handle async call markers correctly", () => { + class MockGeneratorWithAsyncCalls extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + return { + label: this.field.label, + body: { + single_async: { __async_call: 'await getModel("Terminal")' }, + array_with_async: [ + { __async_call: 'await getModel("Terminal")' }, + { __async_call: 'await getBlock("TestBlock")' }, + ], + nested_async: { + validators: { + item_types: [{ __async_call: 'await getModel("FooBar")' }], + }, + }, + }, + }; + } + } + + const generator = new MockGeneratorWithAsyncCalls(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('single_async: await getModel("Terminal")'); + expect(methodCall).toContain( + 'await getModel("Terminal"), await getBlock("TestBlock")', + ); + expect(methodCall).toContain('await getModel("FooBar")'); + expect(methodCall).not.toContain("__async_call"); + }); + + it("should handle mixed async calls and regular values", () => { + class MockGeneratorWithMixed extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + return { + label: this.field.label, + body: { + regular_string: "normal value", + async_call: { __async_call: 'await getModel("Terminal")' }, + number_value: 42, + mixed_array: [ + "string", + { __async_call: 'await getBlock("TestBlock")' }, + 123, + true, + ], + }, + }; + } + } + + const generator = new MockGeneratorWithMixed(config); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain('regular_string: "normal value"'); + expect(methodCall).toContain('async_call: await getModel("Terminal")'); + expect(methodCall).toContain("number_value: 42"); + expect(methodCall).toContain( + '"string", await getBlock("TestBlock"), 123, true', + ); + }); + }); + + describe("convertItemTypeIdsToGetCalls", () => { + it("should convert item type IDs to getModel calls for models", () => { + const itemTypeReferences = new Map([ + ["terminal-id", createMockItemType("Terminal")], + ["airline-id", createMockItemType("Airline")], + ]); + + class TestGenerator extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const calls = this.convertItemTypeIdsToGetCalls([ + "terminal-id", + "airline-id", + ]); + return { + label: this.field.label, + body: { + item_types: calls, + }, + }; + } + } + + const generator = new TestGenerator({ + field: mockField, + itemTypeReferences, + }); + + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain('await getModel("Terminal")'); + expect(methodCall).toContain('await getModel("Airline")'); + }); + + it("should convert item type IDs to getBlock calls for blocks", () => { + const itemTypeReferences = new Map([ + ["block-id", createMockItemType("Test Block", true)], + ]); + + class TestGenerator extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const calls = this.convertItemTypeIdsToGetCalls(["block-id"]); + return { + label: this.field.label, + body: { + item_types: calls, + }, + }; + } + } + + const generator = new TestGenerator({ + field: mockField, + itemTypeReferences, + }); + + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain('await getBlock("TestBlock")'); + }); + + it("should handle complex names with PascalCase conversion", () => { + const itemTypeReferences = new Map([ + ["foobar-id", createMockItemType("Foo Bar")], + ["multi-word-id", createMockItemType("Multi Word Block Name", true)], + ]); + + class TestGenerator extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const calls = this.convertItemTypeIdsToGetCalls([ + "foobar-id", + "multi-word-id", + ]); + return { + label: this.field.label, + body: { + item_types: calls, + }, + }; + } + } + + const generator = new TestGenerator({ + field: mockField, + itemTypeReferences, + }); + + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain('await getModel("FooBar")'); + expect(methodCall).toContain('await getBlock("MultiWordBlockName")'); + }); + + it("should throw error when item type reference is missing", () => { + class TestGenerator extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const calls = this.convertItemTypeIdsToGetCalls(["nonexistent-id"]); + return { + label: this.field.label, + body: { + item_types: calls, + }, + }; + } + } + + const generator = new TestGenerator({ + field: mockField, + itemTypeReferences: new Map(), + }); + + expect(() => generator.generateMethodCall()).toThrow( + 'Field test-date-api-key (date): Item type with ID "nonexistent-id" not found in references', + ); + }); + + it("should throw error when itemTypeReferences is not provided", () => { + class TestGenerator extends FieldGenerator<"addDate"> { + constructor(config: FieldGeneratorConfig) { + super(config); + } + + getMethodCallName() { + return "addDate" as const; + } + + // biome-ignore lint/suspicious/noExplicitAny: Test mock needs to return any type for flexibility + generateBuildConfig(): any { + const calls = this.convertItemTypeIdsToGetCalls(["some-id"]); + return { + label: this.field.label, + body: { + item_types: calls, + }, + }; + } + } + + const generator = new TestGenerator({ + field: mockField, + }); + + expect(() => generator.generateMethodCall()).toThrow( + "Field test-date-api-key (date): Cannot resolve item type references - references not available", + ); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/FieldGenerator.ts b/src/FileGeneration/FieldGenerators/FieldGenerator.ts new file mode 100644 index 0000000..b1e0052 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/FieldGenerator.ts @@ -0,0 +1,611 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import type { + ItemTypeBuilderAddMethods, + MethodNameToConfig, +} from "@/types/ItemTypeBuilderFields"; + +export interface FieldGeneratorConfig { + field: Field; + itemTypeReferences?: Map; +} + +/** + * Custom error for field generator operations + */ +export class FieldGeneratorError extends Error { + constructor( + message: string, + public readonly fieldApiKey: string, + public readonly fieldType: string, + public readonly cause?: Error, + ) { + super(`Field ${fieldApiKey} (${fieldType}): ${message}`); + this.name = "FieldGeneratorError"; + } +} + +/** + * Base class for generating ItemTypeBuilder method calls. + * + * Each subclass handles a specific field type and generates the appropriate + * ItemTypeBuilder method call (e.g., .addDate(), .addText(), etc.) + * + * Types are automatically inferred from the TMethodName generic parameter. + * This ensures compile-time safety when ItemTypeBuilder signatures change. + */ +export abstract class FieldGenerator< + TMethodName extends ItemTypeBuilderAddMethods, +> { + protected field: Field; + protected itemTypeReferences?: Map; + + constructor(config: FieldGeneratorConfig) { + this.field = config.field; + this.itemTypeReferences = config.itemTypeReferences; + } + + /** + * Get the name of the ItemTypeBuilder method to call. + * @example "addDate", "addText", "addBoolean" + */ + abstract getMethodCallName(): TMethodName; + + /** + * Generate the configuration object for the ItemTypeBuilder method. + * Transform API field data into the format expected by ItemTypeBuilder. + * Return type is automatically inferred from TMethodName. + */ + abstract generateBuildConfig(): MethodNameToConfig; + + /** + * Template method for building field body. Subclasses can override for custom logic. + * This provides a default implementation that works for most field types. + */ + protected buildFieldBody(): NonNullable< + MethodNameToConfig["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addValidatorsToBody(body); + + return body; + } + + /** + * Template method for adding validators to body. Subclasses can override for specific validators. + */ + protected addValidatorsToBody( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as Record; + + this.processRequiredValidator(validators); + this.processUniqueValidator(validators); + this.processFormatValidator(validators); + this.processLengthValidator(validators); + this.processNumberRangeValidator(validators); + this.processEnumValidator(validators); + + // Allow subclasses to add specific validators + this.addFieldSpecificValidators(validators); + + if (Object.keys(validators).length > 0) { + this.addOptionalProperty(body, "validators", validators); + } + } + + /** + * Hook for subclasses to add field-specific validators. + * Default implementation does nothing. + */ + protected addFieldSpecificValidators( + _validators: Record, + ): void { + // Default: no field-specific validators + } + + /** + * Template method for generating configuration. Provides default implementation. + */ + protected generateTemplateConfig(): MethodNameToConfig { + const config = this.createBaseConfig() as MethodNameToConfig; + const body = this.buildFieldBody(); + + if (this.hasBodyContent(body)) { + this.addOptionalProperty(config, "body", body); + } + + return this.customizeConfig(config); + } + + /** + * Hook for subclasses to customize the final configuration. + * Default implementation returns config as-is. + */ + protected customizeConfig( + config: MethodNameToConfig, + ): MethodNameToConfig { + return config; + } + + /** + * Generate the complete method call string for the ItemTypeBuilder. + * @returns String like ".addDate({label: 'My Date', body: {...}})" + */ + public generateMethodCall(): string { + const methodName = this.getMethodCallName(); + const config = this.generateBuildConfig(); + + return `.${methodName}(${this.serializeConfig(config)})`; + } + + /** + * Check if the field has any validators defined. + */ + protected hasValidators(): boolean { + return ( + this.field.validators && Object.keys(this.field.validators).length > 0 + ); + } + + /** + * Add hint to body if present in the field. + */ + protected addHintToBody>(body: T): void { + if (this.field.hint) { + (body as any).hint = this.field.hint; + } + } + + /** + * Add default value to body if present in the field. + */ + protected addDefaultValueToBody>( + body: T, + ): void { + if ( + this.field.default_value !== null && + this.field.default_value !== undefined + ) { + (body as any).default_value = this.field.default_value; + } + } + + /** + * Create base body object with common properties. + */ + protected createBaseBody(): { api_key: string } { + return { + api_key: this.field.api_key, + }; + } + + /** + * Create base config object with label. + */ + protected createBaseConfig(): { label: string } { + return { + label: this.field.label, + }; + } + + /** + * Process required validator if present. + * Sets validators.required = true if the field has a required validator. + * Generic to maintain type safety with specific validator types. + */ + protected processRequiredValidator( + validators: T, + ): void { + if (this.field.validators?.["required"]) { + this.addOptionalProperty(validators, "required", true); + } + } + + /** + * Process unique validator if present. + * Sets validators.unique = true if the field has a unique validator. + * Generic to maintain type safety with specific validator types. + */ + protected processUniqueValidator( + validators: T, + ): void { + if (this.field.validators?.["unique"]) { + this.addOptionalProperty(validators, "unique", true); + } + } + + /** + * Process format validator if present, converting string patterns to RegExp. + * Handles custom_pattern conversion from string to RegExp. + * Generic to maintain type safety with specific validator types. + */ + protected processFormatValidator( + validators: T, + ): void { + const formatValidator = this.field.validators?.["format"] as any; + if (!formatValidator) { + return; + } + + const processedFormat: any = {}; + + // Copy predefined patterns as-is + if (formatValidator.predefined_pattern) { + processedFormat.predefined_pattern = formatValidator.predefined_pattern; + } + + // Convert custom_pattern from string to RegExp + if (formatValidator.custom_pattern) { + try { + // If it's already a RegExp, keep it + if (formatValidator.custom_pattern instanceof RegExp) { + processedFormat.custom_pattern = formatValidator.custom_pattern; + } else if ( + typeof formatValidator.custom_pattern === "string" && + formatValidator.custom_pattern.trim() + ) { + // Convert string to RegExp + processedFormat.custom_pattern = new RegExp( + formatValidator.custom_pattern, + ); + } else if ( + typeof formatValidator.custom_pattern === "object" && + Object.keys(formatValidator.custom_pattern).length === 0 + ) { + // Skip empty objects {} that come from DatoCMS API when RegExp isn't properly stored + console.warn( + `Skipping empty custom_pattern object for field ${this.field.api_key}`, + ); + } else { + // If it's something else, skip it completely + console.warn( + `Skipping invalid custom_pattern for field ${this.field.api_key}: not a string or RegExp, got:`, + typeof formatValidator.custom_pattern, + ); + } + } catch (error) { + // If RegExp conversion fails, log error and skip + console.warn( + `Failed to convert custom_pattern to RegExp for field ${this.field.api_key}:`, + error, + ); + } + } + + // Copy description if present + if (formatValidator.description) { + processedFormat.description = formatValidator.description; + } + + if (Object.keys(processedFormat).length > 0) { + this.addOptionalProperty(validators, "format", processedFormat); + } + } + + /** + * Process length validator if present. + * Copies length validator data as-is since it's already in correct format. + */ + protected processLengthValidator( + validators: T, + ): void { + if (this.field.validators?.["length"]) { + this.addOptionalProperty( + validators, + "length", + this.field.validators["length"], + ); + } + } + + /** + * Process number_range validator if present. + * Copies number_range validator data as-is since it's already in correct format. + */ + protected processNumberRangeValidator( + validators: T, + ): void { + if (this.field.validators?.["number_range"]) { + this.addOptionalProperty( + validators, + "number_range", + this.field.validators["number_range"], + ); + } + } + + /** + * Process enum validator if present. + * Copies enum validator data as-is since it's already in correct format. + */ + protected processEnumValidator( + validators: T, + ): void { + if (this.field.validators?.["enum"]) { + this.addOptionalProperty( + validators, + "enum", + this.field.validators["enum"], + ); + } + } + + /** + * Check if body has any content (typically used to decide whether to include body property). + * Generic to maintain type safety with specific body types. + */ + protected hasBodyContent>( + body: T, + ): boolean { + return Object.keys(body).length > 0; + } + + /** + * Type-safe helper to add optional properties to objects + */ + protected addOptionalProperty( + obj: T, + key: K, + value: unknown, + ): void { + if (value !== null && value !== undefined) { + (obj as any)[key] = value; + } + } + + /** + * Validate field configuration before processing + */ + protected validateField(): void { + if (!this.field.api_key) { + throw new FieldGeneratorError( + "Field must have an api_key", + this.field.api_key || "unknown", + this.field.field_type, + ); + } + + if (!this.field.label) { + throw new FieldGeneratorError( + "Field must have a label", + this.field.api_key, + this.field.field_type, + ); + } + } + + /** + * Extract appearance parameters with type safety + */ + protected extractAppearanceParameter( + parameterName: string, + expectedType: "string" | "boolean" | "number" | "array" | "object", + defaultValue?: T, + ): T | undefined { + const parameters = this.field.appearance?.parameters as any; + if (!parameters || !(parameterName in parameters)) { + return defaultValue; + } + + const value = parameters[parameterName]; + + // Type validation + switch (expectedType) { + case "string": + return ( + typeof value === "string" && value.trim() ? value : defaultValue + ) as T | undefined; + case "boolean": + return (typeof value === "boolean" ? value : defaultValue) as + | T + | undefined; + case "number": + return (typeof value === "number" ? value : defaultValue) as + | T + | undefined; + case "array": + return (Array.isArray(value) ? value : defaultValue) as T | undefined; + case "object": + return ( + value && typeof value === "object" && !Array.isArray(value) + ? value + : defaultValue + ) as T | undefined; + default: + return (value ?? defaultValue) as T | undefined; + } + } + + /** + * Extract multiple appearance parameters efficiently + */ + protected extractAppearanceParameters>( + parameterMap: Record< + keyof T, + { + type: "string" | "boolean" | "number" | "array" | "object"; + default?: any; + } + >, + ): Partial { + const result: Partial = {}; + + for (const [key, config] of Object.entries(parameterMap)) { + const value = this.extractAppearanceParameter( + key, + config.type, + config.default, + ); + if (value !== undefined) { + result[key as keyof T] = value; + } + } + + return result; + } + + /** + * Create a Date object with preserved original string for code generation. + * Allows specifying when to preserve the original string format. + */ + protected createPreservedDateValue( + dateString: string, + shouldPreserveString: boolean = true, + ): Date & { _originalString?: string } { + const date = new Date(dateString) as Date & { _originalString?: string }; + + if (shouldPreserveString) { + date._originalString = dateString; + } + + return date; + } + + /** + * Generic helper to process range validators for date-like fields. + * Handles both date_range and date_time_range validators. + * Generic to maintain type safety - requires the range key to exist in validator type. + */ + protected processRangeValidator< + T extends Record, + K extends keyof T, + >( + validators: T, + rangeValidatorKey: K, + shouldPreserveString: boolean | ((dateString: string) => boolean) = true, + ): void { + const rangeData = this.field.validators?.[rangeValidatorKey as string] as + | { min?: string; max?: string } + | undefined; + + if (!rangeData) { + return; + } + + const rangeValidator: { min?: Date; max?: Date } = {}; + + if (rangeData.min) { + const preserve = + typeof shouldPreserveString === "function" + ? shouldPreserveString(rangeData.min) + : shouldPreserveString; + rangeValidator.min = this.createPreservedDateValue( + rangeData.min, + preserve, + ); + } + + if (rangeData.max) { + const preserve = + typeof shouldPreserveString === "function" + ? shouldPreserveString(rangeData.max) + : shouldPreserveString; + rangeValidator.max = this.createPreservedDateValue( + rangeData.max, + preserve, + ); + } + + if (Object.keys(rangeValidator).length > 0) { + validators[rangeValidatorKey] = rangeValidator as T[K]; + } + } + + // biome-ignore lint/suspicious/noExplicitAny: Serialization method needs to handle any type of configuration object + private serializeConfig(obj: any): string { + if (obj === null) return "null"; + if (obj === undefined) return "undefined"; + if (typeof obj === "string") return JSON.stringify(obj); + if (typeof obj === "number" || typeof obj === "boolean") return String(obj); + if (obj instanceof RegExp) { + return obj.toString(); + } + if (obj instanceof Date) { + // Check if this Date has an original string we should preserve + // biome-ignore lint/suspicious/noExplicitAny: Date object may have custom _originalString property + const originalString = (obj as any)._originalString; + if (originalString) { + return `new Date(${JSON.stringify(originalString)})`; + } + return `new Date(${JSON.stringify(obj.toISOString())})`; + } + + if (Array.isArray(obj)) { + return `[${obj.map((item) => this.serializeConfig(item)).join(", ")}]`; + } + + if (typeof obj === "object") { + // Handle async call markers + if (obj.__async_call) { + return obj.__async_call; + } + + // Skip empty objects that might be invalid RegExp patterns + if (Object.keys(obj).length === 0) { + return "{}"; + } + + const entries = Object.entries(obj) + .map(([key, value]) => `${key}: ${this.serializeConfig(value)}`) + .join(", "); + return `{ ${entries} }`; + } + + return JSON.stringify(obj); + } + + /** + * Convert item type IDs to getModel() or getBlock() calls + */ + protected convertItemTypeIdsToGetCalls( + itemTypeIds: string[], + ): Array<{ __async_call: string }> { + if (!this.itemTypeReferences) { + // Throw error if no references available + throw new FieldGeneratorError( + "Cannot resolve item type references - references not available", + this.field.api_key, + this.field.field_type, + ); + } + + return itemTypeIds.map((id) => { + const itemType = this.itemTypeReferences!.get(id); + if (!itemType) { + // Throw error if item type not found + throw new FieldGeneratorError( + `Item type with ID "${id}" not found in references`, + this.field.api_key, + this.field.field_type, + ); + } + + const functionName = itemType.modular_block ? "getBlock" : "getModel"; + const apiKey = this.toPascalCase(itemType.name); + return { __async_call: `await ${functionName}("${apiKey}")` }; + }); + } + + /** + * Convert a string to PascalCase + */ + private toPascalCase(str: string): string { + return str + .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => { + return word.toUpperCase(); + }) + .replace(/\s+/g, ""); + } +} diff --git a/src/FileGeneration/FieldGenerators/FieldGeneratorFactory.test.ts b/src/FileGeneration/FieldGenerators/FieldGeneratorFactory.test.ts new file mode 100644 index 0000000..4790c58 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/FieldGeneratorFactory.test.ts @@ -0,0 +1,860 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { BooleanFieldGenerator } from "@/FileGeneration/FieldGenerators/BooleanFieldGenerator"; +import { BooleanRadioGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator"; +import { ColorPickerFieldGenerator } from "@/FileGeneration/FieldGenerators/ColorPickerFieldGenerator"; +import { DateFieldGenerator } from "@/FileGeneration/FieldGenerators/DateFieldGenerator"; +import { DateTimeFieldGenerator } from "@/FileGeneration/FieldGenerators/DateTimeFieldGenerator"; +import { EmailFieldGenerator } from "@/FileGeneration/FieldGenerators/EmailFieldGenerator"; +import { ExternalVideoFieldGenerator } from "@/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator"; +import { FieldGeneratorFactory } from "@/FileGeneration/FieldGenerators/FieldGeneratorFactory"; +import { FloatFieldGenerator } from "@/FileGeneration/FieldGenerators/FloatFieldGenerator"; +import { GalleryFieldGenerator } from "@/FileGeneration/FieldGenerators/GalleryFieldGenerator"; +import { IntegerFieldGenerator } from "@/FileGeneration/FieldGenerators/IntegerFieldGenerator"; +import { JsonFieldGenerator } from "@/FileGeneration/FieldGenerators/JsonFieldGenerator"; +import { LinkFieldGenerator } from "@/FileGeneration/FieldGenerators/LinkFieldGenerator"; +import { LinksFieldGenerator } from "@/FileGeneration/FieldGenerators/LinksFieldGenerator"; +import { LocationFieldGenerator } from "@/FileGeneration/FieldGenerators/LocationFieldGenerator"; +import { MarkdownFieldGenerator } from "@/FileGeneration/FieldGenerators/MarkdownFieldGenerator"; +import { MultiLineTextFieldGenerator } from "@/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator"; +import { RichTextFieldGenerator } from "@/FileGeneration/FieldGenerators/RichTextFieldGenerator"; +import { SeoFieldGenerator } from "@/FileGeneration/FieldGenerators/SeoFieldGenerator"; +import { SingleAssetFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleAssetFieldGenerator"; +import { SingleBlockFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleBlockFieldGenerator"; +import { SingleLineStringFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator"; +import { SlugFieldGenerator } from "@/FileGeneration/FieldGenerators/SlugFieldGenerator"; +import { StringCheckboxGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator"; +import { StringMultiSelectFieldGenerator } from "@/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator"; +import { StringRadioGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator"; +import { StringSelectFieldGenerator } from "@/FileGeneration/FieldGenerators/StringSelectFieldGenerator"; +import { StructuredTextFieldGenerator } from "@/FileGeneration/FieldGenerators/StructuredTextFieldGenerator"; +import { TextareaFieldGenerator } from "@/FileGeneration/FieldGenerators/TextareaFieldGenerator"; +import { UrlFieldGenerator } from "@/FileGeneration/FieldGenerators/UrlFieldGenerator"; +import { WysiwygFieldGenerator } from "@/FileGeneration/FieldGenerators/WysiwygFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("FieldGeneratorFactory", () => { + let factory: FieldGeneratorFactory; + + beforeEach(() => { + factory = new FieldGeneratorFactory(); + }); + + describe("Non-text field type mappings", () => { + it("creates ColorPickerFieldGenerator for color fields", () => { + const field = createMockField({ + label: "Color", + api_key: "color", + field_type: "color", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(ColorPickerFieldGenerator); + }); + + it("creates DateFieldGenerator for date fields", () => { + const field = createMockField({ + label: "Date", + api_key: "date", + field_type: "date", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(DateFieldGenerator); + }); + + it("creates DateTimeFieldGenerator for date_time fields", () => { + const field = createMockField({ + label: "DateTime", + api_key: "datetime", + field_type: "date_time", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(DateTimeFieldGenerator); + }); + + it("creates SingleAssetFieldGenerator for file fields", () => { + const field = createMockField({ + label: "File", + api_key: "file", + field_type: "file", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(SingleAssetFieldGenerator); + }); + + it("creates FloatFieldGenerator for float fields", () => { + const field = createMockField({ + label: "Float", + api_key: "float", + field_type: "float", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(FloatFieldGenerator); + }); + + it("creates GalleryFieldGenerator for gallery fields", () => { + const field = createMockField({ + label: "Gallery", + api_key: "gallery", + field_type: "gallery", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(GalleryFieldGenerator); + }); + + it("creates IntegerFieldGenerator for integer fields", () => { + const field = createMockField({ + label: "Integer", + api_key: "integer", + field_type: "integer", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(IntegerFieldGenerator); + }); + + it("creates LocationFieldGenerator for lat_lon fields", () => { + const field = createMockField({ + label: "Location", + api_key: "location", + field_type: "lat_lon", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(LocationFieldGenerator); + }); + + it("creates SeoFieldGenerator for seo fields", () => { + const field = createMockField({ + label: "SEO", + api_key: "seo", + field_type: "seo", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(SeoFieldGenerator); + }); + + it("creates SlugFieldGenerator for slug fields", () => { + const field = createMockField({ + label: "Slug", + api_key: "slug", + field_type: "slug", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(SlugFieldGenerator); + }); + + it("creates ExternalVideoFieldGenerator for video fields", () => { + const field = createMockField({ + label: "Video", + api_key: "video", + field_type: "video", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(ExternalVideoFieldGenerator); + }); + + it("creates LinkFieldGenerator for link fields", () => { + const field = createMockField({ + label: "Link", + api_key: "link", + field_type: "link", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(LinkFieldGenerator); + }); + + it("creates LinksFieldGenerator for links fields", () => { + const field = createMockField({ + label: "Links", + api_key: "links", + field_type: "links", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(LinksFieldGenerator); + }); + }); + + describe("Boolean field type mappings", () => { + it("creates BooleanFieldGenerator for boolean fields with no editor", () => { + const field = createMockField({ + label: "Boolean", + api_key: "boolean", + field_type: "boolean", + appearance: undefined as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(BooleanFieldGenerator); + }); + + it("creates BooleanRadioGroupFieldGenerator for boolean fields with boolean_radio_group editor", () => { + const field = createMockField({ + label: "Boolean Radio Group", + api_key: "boolean_radio_group", + field_type: "boolean", + appearance: { + addons: [], + editor: "boolean_radio_group", + parameters: { + positive_radio: { label: "Yes" }, + negative_radio: { label: "No" }, + }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(BooleanRadioGroupFieldGenerator); + }); + + it("creates BooleanFieldGenerator for boolean fields with unknown editor", () => { + const field = createMockField({ + label: "Unknown Editor Boolean", + api_key: "unknown_editor_boolean", + field_type: "boolean", + appearance: { + addons: [], + editor: "unknown_editor" as any, + parameters: {}, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(BooleanFieldGenerator); + }); + }); + + describe("Text field type mappings", () => { + it("creates MarkdownFieldGenerator for text fields with markdown editor", () => { + const field = createMockField({ + label: "Markdown", + api_key: "markdown", + field_type: "text", + appearance: { + addons: [], + editor: "markdown", + parameters: { toolbar: ["bold", "italic"] }, + }, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(MarkdownFieldGenerator); + }); + + it("creates WysiwygFieldGenerator for text fields with wysiwyg editor", () => { + const field = createMockField({ + label: "Wysiwyg", + api_key: "wysiwyg", + field_type: "text", + appearance: { + addons: [], + editor: "wysiwyg", + parameters: { toolbar: ["bold", "italic"] }, + }, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(WysiwygFieldGenerator); + }); + + it("creates TextareaFieldGenerator for text fields with textarea editor", () => { + const field = createMockField({ + label: "Textarea", + api_key: "textarea", + field_type: "text", + appearance: { + addons: [], + editor: "textarea", + parameters: { placeholder: "Enter text..." }, + }, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(TextareaFieldGenerator); + }); + + it("creates MultiLineTextFieldGenerator for text fields with no editor", () => { + const field = createMockField({ + label: "MultiLineText", + api_key: "multi_line_text", + field_type: "text", + appearance: undefined as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(MultiLineTextFieldGenerator); + }); + + it("creates MultiLineTextFieldGenerator for text fields with empty appearance", () => { + const field = createMockField({ + label: "MultiLineText", + api_key: "multi_line_text", + field_type: "text", + appearance: { addons: [], editor: "plain", parameters: {} } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(MultiLineTextFieldGenerator); + }); + + it("creates MultiLineTextFieldGenerator for text fields with unknown editor", () => { + const field = createMockField({ + label: "Unknown Editor", + api_key: "unknown_editor", + field_type: "text", + appearance: { + addons: [], + editor: "unknown_editor" as any, + parameters: {}, + }, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(MultiLineTextFieldGenerator); + }); + }); + + describe("String field type mappings", () => { + it("creates EmailFieldGenerator for string fields with email format validator", () => { + const field = createMockField({ + label: "Email", + api_key: "email", + field_type: "string", + validators: { + format: { + predefined_pattern: "email", + }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(EmailFieldGenerator); + }); + + it("creates UrlFieldGenerator for string fields with url format validator", () => { + const field = createMockField({ + label: "URL", + api_key: "url", + field_type: "string", + validators: { + format: { + predefined_pattern: "url", + }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(UrlFieldGenerator); + }); + + it("creates SingleLineStringFieldGenerator for string fields with single_line editor", () => { + const field = createMockField({ + label: "Single Line String", + api_key: "single_line_string", + field_type: "string", + appearance: { + addons: [], + editor: "single_line", + parameters: { heading: false, placeholder: "" }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(SingleLineStringFieldGenerator); + }); + + it("creates StringRadioGroupFieldGenerator for string fields with string_radio_group editor", () => { + const field = createMockField({ + label: "String Radio Group", + api_key: "string_radio_group", + field_type: "string", + appearance: { + addons: [], + editor: "string_radio_group", + parameters: { + radios: [ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2" }, + ], + }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(StringRadioGroupFieldGenerator); + }); + + it("creates StringSelectFieldGenerator for string fields with string_select editor", () => { + const field = createMockField({ + label: "String Select", + api_key: "string_select", + field_type: "string", + appearance: { + addons: [], + editor: "string_select", + parameters: { + options: [ + { label: "Choice 1", value: "choice1" }, + { label: "Choice 2", value: "choice2" }, + ], + }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(StringSelectFieldGenerator); + }); + + it("creates SingleLineStringFieldGenerator for string fields with no editor", () => { + const field = createMockField({ + label: "Plain String", + api_key: "plain_string", + field_type: "string", + appearance: undefined as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(SingleLineStringFieldGenerator); + }); + + it("creates SingleLineStringFieldGenerator for string fields with unknown editor", () => { + const field = createMockField({ + label: "Unknown Editor String", + api_key: "unknown_editor_string", + field_type: "string", + appearance: { + addons: [], + editor: "unknown_editor" as any, + parameters: {}, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(SingleLineStringFieldGenerator); + }); + }); + + describe("JSON field type mappings", () => { + it("creates JsonFieldGenerator for json fields with no editor", () => { + const field = createMockField({ + label: "JSON", + api_key: "json", + field_type: "json", + appearance: undefined as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(JsonFieldGenerator); + }); + + it("creates StringMultiSelectFieldGenerator for json fields with string_multi_select editor", () => { + const field = createMockField({ + label: "String Multi Select", + api_key: "string_multi_select", + field_type: "json", + appearance: { + addons: [], + editor: "string_multi_select", + parameters: { + options: [ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2" }, + ], + }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(StringMultiSelectFieldGenerator); + }); + + it("creates StringCheckboxGroupFieldGenerator for json fields with string_checkbox_group editor", () => { + const field = createMockField({ + label: "String Checkbox Group", + api_key: "string_checkbox_group", + field_type: "json", + appearance: { + addons: [], + editor: "string_checkbox_group", + parameters: { + options: [ + { label: "Feature A", value: "feature_a" }, + { label: "Feature B", value: "feature_b" }, + ], + }, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(StringCheckboxGroupFieldGenerator); + }); + + it("creates JsonFieldGenerator for json fields with unknown editor", () => { + const field = createMockField({ + label: "Unknown Editor JSON", + api_key: "unknown_editor_json", + field_type: "json", + appearance: { + addons: [], + editor: "unknown_editor" as any, + parameters: {}, + } as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(JsonFieldGenerator); + }); + }); + + describe("New field type mappings", () => { + it("creates RichTextFieldGenerator for rich_text fields", () => { + const field = createMockField({ + label: "Rich Text", + api_key: "rich_text", + field_type: "rich_text", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(RichTextFieldGenerator); + }); + + it("creates SingleBlockFieldGenerator for single_block fields", () => { + const field = createMockField({ + label: "Single Block", + api_key: "single_block", + field_type: "single_block", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(SingleBlockFieldGenerator); + }); + + it("creates StructuredTextFieldGenerator for structured_text fields", () => { + const field = createMockField({ + label: "Structured Text", + api_key: "structured_text", + field_type: "structured_text", + }); + + const generator = factory.createGenerator({ field }); + expect(generator).toBeInstanceOf(StructuredTextFieldGenerator); + }); + }); + + describe("Error handling", () => { + it("throws error for unsupported field type", () => { + const field = createMockField({ + label: "Unsupported", + api_key: "unsupported", + field_type: "unsupported_type" as any, + }); + + expect(() => factory.createGenerator({ field })).toThrow( + "No generator found for field type: unsupported_type", + ); + }); + + it("throws error for null field_type", () => { + const field = createMockField({ + label: "Null Type", + api_key: "null_type", + field_type: null as any, + }); + + expect(() => factory.createGenerator({ field })).toThrow( + "No generator found for field type: null", + ); + }); + + it("throws error for undefined field_type", () => { + const field = createMockField({ + label: "Undefined Type", + api_key: "undefined_type", + field_type: undefined as any, + }); + + expect(() => factory.createGenerator({ field })).toThrow( + "No generator found for field type: undefined", + ); + }); + }); + + describe("Generator consistency", () => { + it("all generators have correct method names", () => { + const testCases = [ + { fieldType: "color", expected: "addColorPicker" }, + { fieldType: "date", expected: "addDate" }, + { fieldType: "date_time", expected: "addDateTime" }, + { fieldType: "file", expected: "addSingleAsset" }, + { fieldType: "float", expected: "addFloat" }, + { fieldType: "gallery", expected: "addAssetGallery" }, + { fieldType: "integer", expected: "addInteger" }, + { fieldType: "lat_lon", expected: "addLocation" }, + { fieldType: "link", expected: "addLink" }, + { fieldType: "links", expected: "addLinks" }, + { fieldType: "seo", expected: "addSeo" }, + { fieldType: "slug", expected: "addSlug" }, + { fieldType: "video", expected: "addExternalVideo" }, + { fieldType: "rich_text", expected: "addModularContent" }, + { fieldType: "single_block", expected: "addSingleBlock" }, + { fieldType: "structured_text", expected: "addStructuredText" }, + ]; + + testCases.forEach(({ fieldType, expected }) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: fieldType as any, + }); + + const generator = factory.createGenerator({ field }); + expect(generator.getMethodCallName()).toBe(expected); + }); + }); + + it("all boolean generators have correct method names", () => { + const testCases = [ + { editor: "boolean_radio_group", expected: "addBooleanRadioGroup" }, + { editor: undefined, expected: "addBoolean" }, + ]; + + testCases.forEach(({ editor, expected }) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "boolean", + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + expect(generator.getMethodCallName()).toBe(expected); + }); + }); + + it("all text generators have correct method names", () => { + const testCases = [ + { editor: "markdown", expected: "addMarkdown" }, + { editor: "wysiwyg", expected: "addWysiwyg" }, + { editor: "textarea", expected: "addTextarea" }, + { editor: undefined, expected: "addMultiLineText" }, + ]; + + testCases.forEach(({ editor, expected }) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "text", + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + expect(generator.getMethodCallName()).toBe(expected); + }); + }); + + it("all json generators have correct method names", () => { + const testCases = [ + { editor: "string_multi_select", expected: "addStringMultiSelect" }, + { editor: "string_checkbox_group", expected: "addStringCheckboxGroup" }, + { editor: undefined, expected: "addJson" }, + ]; + + testCases.forEach(({ editor, expected }) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "json", + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + expect(generator.getMethodCallName()).toBe(expected); + }); + }); + + it("all string generators have correct method names", () => { + const testCases = [ + { + validators: { format: { predefined_pattern: "email" } }, + expected: "addEmail", + }, + { + validators: { format: { predefined_pattern: "url" } }, + expected: "addUrl", + }, + { editor: "single_line", expected: "addSingleLineString" }, + { editor: "string_radio_group", expected: "addStringRadioGroup" }, + { editor: "string_select", expected: "addStringSelect" }, + { editor: undefined, expected: "addSingleLineString" }, + ]; + + testCases.forEach(({ validators, editor, expected }) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "string", + ...(validators && { validators: validators as any }), + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + expect(generator.getMethodCallName()).toBe(expected); + }); + }); + + it("all generators can generate method calls", () => { + const fieldTypes = [ + "color", + "date", + "date_time", + "file", + "float", + "gallery", + "integer", + "lat_lon", + "link", + "links", + "seo", + "slug", + "video", + ]; + + fieldTypes.forEach((fieldType) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: fieldType as any, + }); + + const generator = factory.createGenerator({ field }); + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain(`.${generator.getMethodCallName()}(`); + }); + }); + + it("all boolean generators can generate method calls", () => { + const editors = ["boolean_radio_group", undefined]; + + editors.forEach((editor) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "boolean", + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain(`.${generator.getMethodCallName()}(`); + }); + }); + + it("all text generators can generate method calls", () => { + const editors = ["markdown", "wysiwyg", "textarea", undefined]; + + editors.forEach((editor) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "text", + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain(`.${generator.getMethodCallName()}(`); + }); + }); + + it("all json generators can generate method calls", () => { + const editors = [ + "string_multi_select", + "string_checkbox_group", + undefined, + ]; + + editors.forEach((editor) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "json", + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain(`.${generator.getMethodCallName()}(`); + }); + }); + + it("all string generators can generate method calls", () => { + const testCases = [ + { validators: { format: { predefined_pattern: "email" } } }, + { validators: { format: { predefined_pattern: "url" } } }, + { editor: "single_line" }, + { editor: "string_radio_group" }, + { editor: "string_select" }, + { editor: undefined }, + ]; + + testCases.forEach(({ validators, editor }) => { + const field = createMockField({ + label: "Test", + api_key: "test", + field_type: "string", + ...(validators && { validators: validators as any }), + appearance: editor + ? ({ addons: [], editor, parameters: {} } as any) + : (undefined as any), + }); + + const generator = factory.createGenerator({ field }); + const methodCall = generator.generateMethodCall(); + expect(methodCall).toContain(`.${generator.getMethodCallName()}(`); + }); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/FieldGeneratorFactory.ts b/src/FileGeneration/FieldGenerators/FieldGeneratorFactory.ts new file mode 100644 index 0000000..98ee6cd --- /dev/null +++ b/src/FileGeneration/FieldGenerators/FieldGeneratorFactory.ts @@ -0,0 +1,238 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { BooleanFieldGenerator } from "@/FileGeneration/FieldGenerators/BooleanFieldGenerator"; +import { BooleanRadioGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/BooleanRadioGroupFieldGenerator"; +import { ColorPickerFieldGenerator } from "@/FileGeneration/FieldGenerators/ColorPickerFieldGenerator"; +import { DateFieldGenerator } from "@/FileGeneration/FieldGenerators/DateFieldGenerator"; +import { DateTimeFieldGenerator } from "@/FileGeneration/FieldGenerators/DateTimeFieldGenerator"; +import { EmailFieldGenerator } from "@/FileGeneration/FieldGenerators/EmailFieldGenerator"; +import { ExternalVideoFieldGenerator } from "@/FileGeneration/FieldGenerators/ExternalVideoFieldGenerator"; +import type { + FieldGenerator, + FieldGeneratorConfig, +} from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import { FieldGeneratorError } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import { FloatFieldGenerator } from "@/FileGeneration/FieldGenerators/FloatFieldGenerator"; +import { GalleryFieldGenerator } from "@/FileGeneration/FieldGenerators/GalleryFieldGenerator"; +import { IntegerFieldGenerator } from "@/FileGeneration/FieldGenerators/IntegerFieldGenerator"; +import { JsonFieldGenerator } from "@/FileGeneration/FieldGenerators/JsonFieldGenerator"; +import { LinkFieldGenerator } from "@/FileGeneration/FieldGenerators/LinkFieldGenerator"; +import { LinksFieldGenerator } from "@/FileGeneration/FieldGenerators/LinksFieldGenerator"; +import { LocationFieldGenerator } from "@/FileGeneration/FieldGenerators/LocationFieldGenerator"; +import { MarkdownFieldGenerator } from "@/FileGeneration/FieldGenerators/MarkdownFieldGenerator"; +import { MultiLineTextFieldGenerator } from "@/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator"; +import { RichTextFieldGenerator } from "@/FileGeneration/FieldGenerators/RichTextFieldGenerator"; +import { SeoFieldGenerator } from "@/FileGeneration/FieldGenerators/SeoFieldGenerator"; +import { SingleAssetFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleAssetFieldGenerator"; +import { SingleBlockFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleBlockFieldGenerator"; +import { SingleLineStringFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator"; +import { SlugFieldGenerator } from "@/FileGeneration/FieldGenerators/SlugFieldGenerator"; +import { StringCheckboxGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator"; +import { StringMultiSelectFieldGenerator } from "@/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator"; +import { StringRadioGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator"; +import { StringSelectFieldGenerator } from "@/FileGeneration/FieldGenerators/StringSelectFieldGenerator"; +import { StructuredTextFieldGenerator } from "@/FileGeneration/FieldGenerators/StructuredTextFieldGenerator"; +import { TextareaFieldGenerator } from "@/FileGeneration/FieldGenerators/TextareaFieldGenerator"; +import { UrlFieldGenerator } from "@/FileGeneration/FieldGenerators/UrlFieldGenerator"; +import { WysiwygFieldGenerator } from "@/FileGeneration/FieldGenerators/WysiwygFieldGenerator"; +import type { ItemTypeBuilderAddMethods } from "@/types/ItemTypeBuilderFields"; + +type FieldGeneratorConstructor = new ( + config: FieldGeneratorConfig, +) => FieldGenerator; + +export class FieldGeneratorFactory { + private readonly generatorCache = new Map< + string, + FieldGeneratorConstructor + >(); + + /** + * Main factory method that determines the correct generator based on field type and appearance + */ + createGenerator( + config: FieldGeneratorConfig, + ): FieldGenerator { + const field = config.field; + + try { + const GeneratorClass = this.getGeneratorClass(field); + return new GeneratorClass(config); + } catch (error) { + throw new FieldGeneratorError( + `Failed to create generator: ${error instanceof Error ? error.message : "Unknown error"}`, + field.api_key, + field.field_type, + error instanceof Error ? error : undefined, + ); + } + } + + /** + * Complex mapping logic to determine the correct generator class with caching + */ + private getGeneratorClass(field: Field): FieldGeneratorConstructor { + const cacheKey = this.createCacheKey(field); + + if (this.generatorCache.has(cacheKey)) { + return this.generatorCache.get(cacheKey)!; + } + + let generatorClass: FieldGeneratorConstructor; + + // Handle text fields with special logic for appearance.editor + if (field.field_type === "text") { + generatorClass = this.getTextFieldGenerator(field); + } + // Handle boolean fields with special logic for appearance.editor + else if (field.field_type === "boolean") { + generatorClass = this.getBooleanFieldGenerator(field); + } + // Handle string fields with special logic for appearance.editor and validators + else if (field.field_type === "string") { + generatorClass = this.getStringFieldGenerator(field); + } + // Handle json fields with special logic for appearance.editor + else if (field.field_type === "json") { + generatorClass = this.getJsonFieldGenerator(field); + } else { + generatorClass = this.getStaticFieldGenerator(field); + } + + this.generatorCache.set(cacheKey, generatorClass); + return generatorClass; + } + + /** + * Create a cache key for generator selection + */ + private createCacheKey(field: Field): string { + const editor = field.appearance?.editor || "default"; + const validators = field.validators as any; + + let validatorKey = ""; + if (validators?.format?.predefined_pattern) { + validatorKey = `:${validators.format.predefined_pattern}`; + } + + return `${field.field_type}:${editor}${validatorKey}`; + } + + /** + * Handle static field type mappings + */ + private getStaticFieldGenerator(field: Field): FieldGeneratorConstructor { + const generatorMap: Record< + Exclude, + FieldGeneratorConstructor + > = { + color: ColorPickerFieldGenerator, + date: DateFieldGenerator, + date_time: DateTimeFieldGenerator, + file: SingleAssetFieldGenerator, + float: FloatFieldGenerator, + gallery: GalleryFieldGenerator, + integer: IntegerFieldGenerator, + lat_lon: LocationFieldGenerator, + link: LinkFieldGenerator, + rich_text: RichTextFieldGenerator, + single_block: SingleBlockFieldGenerator, + structured_text: StructuredTextFieldGenerator, + links: LinksFieldGenerator, + seo: SeoFieldGenerator, + slug: SlugFieldGenerator, + video: ExternalVideoFieldGenerator, + }; + + const generatorClass = + generatorMap[field.field_type as keyof typeof generatorMap]; + + if (!generatorClass) { + throw new FieldGeneratorError( + `No generator found for field type: ${field.field_type}`, + field.api_key, + field.field_type, + ); + } + + return generatorClass; + } + + /** + * Determine which text field generator to use based on appearance.editor + */ + private getTextFieldGenerator(field: Field): FieldGeneratorConstructor { + const editor = field.appearance?.editor; + + switch (editor) { + case "markdown": + return MarkdownFieldGenerator; + case "wysiwyg": + return WysiwygFieldGenerator; + case "textarea": + return TextareaFieldGenerator; + default: + return MultiLineTextFieldGenerator; + } + } + + /** + * Determine which boolean field generator to use based on appearance.editor + */ + private getBooleanFieldGenerator(field: Field): FieldGeneratorConstructor { + const editor = field.appearance?.editor; + + switch (editor) { + case "boolean_radio_group": + return BooleanRadioGroupFieldGenerator; + default: + return BooleanFieldGenerator; + } + } + + /** + * Determine which string field generator to use based on appearance.editor and validators + */ + private getStringFieldGenerator(field: Field): FieldGeneratorConstructor { + const editor = field.appearance?.editor; + const validators = field.validators as any; + + // Check for email format validator + if (validators?.format?.predefined_pattern === "email") { + return EmailFieldGenerator; + } + + // Check for URL format validator + if (validators?.format?.predefined_pattern === "url") { + return UrlFieldGenerator; + } + + // Check appearance.editor for other string field types + switch (editor) { + case "single_line": + return SingleLineStringFieldGenerator; + case "string_radio_group": + return StringRadioGroupFieldGenerator; + case "string_select": + return StringSelectFieldGenerator; + default: + // Default to SingleLineString for plain string fields + return SingleLineStringFieldGenerator; + } + } + + /** + * Determine which JSON field generator to use based on appearance.editor + */ + private getJsonFieldGenerator(field: Field): FieldGeneratorConstructor { + const editor = field.appearance?.editor; + + switch (editor) { + case "string_multi_select": + return StringMultiSelectFieldGenerator; + case "string_checkbox_group": + return StringCheckboxGroupFieldGenerator; + default: + return JsonFieldGenerator; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/FloatFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/FloatFieldGenerator.test.ts new file mode 100644 index 0000000..14ee503 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/FloatFieldGenerator.test.ts @@ -0,0 +1,311 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { FloatConfig } from "@/Fields/Float"; +import { FloatFieldGenerator } from "@/FileGeneration/FieldGenerators/FloatFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "float", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "float", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("FloatFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a float field with label", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Price", + api_key: "price", + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Price", + body: { + api_key: "price", + }, + } satisfies FloatConfig); + }); + + it("does not include position", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float without Position", + api_key: "float_without_position", + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Float without Position", + body: { + api_key: "float_without_position", + }, + } satisfies FloatConfig); + }); + + it("can generate a float field with a hint", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float with Hint", + api_key: "float_with_hint", + hint: "Enter a decimal number", + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Float with Hint", + body: { + api_key: "float_with_hint", + hint: "Enter a decimal number", + }, + } satisfies FloatConfig); + }); + + it("can generate a float field without a hint", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float without Hint", + api_key: "float_without_hint", + hint: null, + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Float without Hint", + body: { + api_key: "float_without_hint", + }, + } satisfies FloatConfig); + }); + + it("should not include validators if none are set", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float without Validators", + api_key: "float_without_validators", + validators: {}, + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Float without Validators", + body: { + api_key: "float_without_validators", + }, + } satisfies FloatConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required float field", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Required Float", + api_key: "required_float", + validators: { required: {} }, + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Required Float", + body: { + api_key: "required_float", + validators: { required: true }, + }, + } satisfies FloatConfig); + }); + + it("can generate a non-required float field", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Non-Required Float", + api_key: "non_required_float", + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Float", + body: { + api_key: "non_required_float", + }, + } satisfies FloatConfig); + }); + }); + + describe("Number range", () => { + it("can generate a float field with number_range validator", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float with Range", + api_key: "float_with_range", + validators: { + number_range: { min: 0.0, max: 100.0 }, + }, + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Float with Range", + body: { + api_key: "float_with_range", + validators: { + number_range: { min: 0.0, max: 100.0 }, + }, + }, + } satisfies FloatConfig); + }); + + it("can generate a float field with multiple validators", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float with Multiple Validators", + api_key: "float_multiple_validators", + validators: { + required: {}, + number_range: { min: 1.5, max: 99.9 }, + }, + }), + }); + + expect(floatGenerator.generateBuildConfig()).toEqual({ + label: "Float with Multiple Validators", + body: { + api_key: "float_multiple_validators", + validators: { + required: true, + number_range: { min: 1.5, max: 99.9 }, + }, + }, + } satisfies FloatConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Test Float", + api_key: "test_float", + }), + }); + + const methodCall = floatGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addFloat\(/); + expect(methodCall).toContain('"Test Float"'); + expect(methodCall).toContain('"test_float"'); + }); + + it("generates method call with validators", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float with Validators", + api_key: "float_with_validators", + validators: { + number_range: { min: 0, max: 100 }, + }, + }), + }); + + const methodCall = floatGenerator.generateMethodCall(); + + expect(methodCall).toContain("number_range"); + expect(methodCall).toMatch(/\.addFloat\(/); + }); + + it("generates method call with hint", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Float with Hint", + api_key: "float_with_hint", + hint: "Enter decimal number", + }), + }); + + const methodCall = floatGenerator.generateMethodCall(); + + expect(methodCall).toContain('"Enter decimal number"'); + expect(methodCall).toMatch(/\.addFloat\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic Float field from DatoCMS", () => { + const apiResponseField: Field = { + id: "float-field-123", + type: "field", + label: "Price", + field_type: "float", + api_key: "price", + hint: "Enter price in USD", + localized: false, + validators: { + required: {}, + number_range: { min: 0.01, max: 9999.99 }, + }, + position: 1, + appearance: { + addons: [], + editor: "float", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const floatGenerator = new FloatFieldGenerator({ + field: apiResponseField, + }); + + const config = floatGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Price", + body: { + api_key: "price", + hint: "Enter price in USD", + validators: { + required: true, + number_range: { min: 0.01, max: 9999.99 }, + }, + }, + } satisfies FloatConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const floatGenerator = new FloatFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(floatGenerator.getMethodCallName()).toBe("addFloat"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/FloatFieldGenerator.ts b/src/FileGeneration/FieldGenerators/FloatFieldGenerator.ts new file mode 100644 index 0000000..4f7a09c --- /dev/null +++ b/src/FileGeneration/FieldGenerators/FloatFieldGenerator.ts @@ -0,0 +1,10 @@ +import { NumberFieldGenerator } from "@/FileGeneration/FieldGenerators/NumberFieldGenerator"; + +/** + * Generates ItemTypeBuilder.addFloat() method calls. + */ +export class FloatFieldGenerator extends NumberFieldGenerator<"addFloat"> { + getMethodCallName() { + return "addFloat" as const; + } +} diff --git a/src/FileGeneration/FieldGenerators/GalleryFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/GalleryFieldGenerator.test.ts new file mode 100644 index 0000000..4f1fdc1 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/GalleryFieldGenerator.test.ts @@ -0,0 +1,401 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { AssetGalleryConfig } from "@/Fields/AssetGallery"; +import { GalleryFieldGenerator } from "@/FileGeneration/FieldGenerators/GalleryFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "gallery", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "gallery", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("GalleryFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a gallery field with label", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Image Gallery", + api_key: "image_gallery", + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Image Gallery", + body: { + api_key: "image_gallery", + }, + } satisfies AssetGalleryConfig); + }); + + it("does not include position", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery without Position", + api_key: "gallery_without_position", + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery without Position", + body: { + api_key: "gallery_without_position", + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field with a hint", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Hint", + api_key: "gallery_with_hint", + hint: "Upload multiple images for the gallery", + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with Hint", + body: { + api_key: "gallery_with_hint", + hint: "Upload multiple images for the gallery", + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field without a hint", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery without Hint", + api_key: "gallery_without_hint", + hint: null, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery without Hint", + body: { + api_key: "gallery_without_hint", + }, + } satisfies AssetGalleryConfig); + }); + + it("should not include validators if none are set", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery without Validators", + api_key: "gallery_without_validators", + validators: {}, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery without Validators", + body: { + api_key: "gallery_without_validators", + }, + } satisfies AssetGalleryConfig); + }); + }); + + describe("With validators", () => { + describe("Gallery-specific validators", () => { + it("can generate a gallery field with size validator", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Size", + api_key: "gallery_with_size", + validators: { + size: { min: 1, max: 10 }, + }, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with Size", + body: { + api_key: "gallery_with_size", + validators: { + size: { min: 1, max: 10 }, + }, + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field with file_size validator", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with File Size", + api_key: "gallery_with_file_size", + validators: { + file_size: { max_value: 5, max_unit: "MB" }, + }, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with File Size", + body: { + api_key: "gallery_with_file_size", + validators: { + file_size: { max_value: 5, max_unit: "MB" }, + }, + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field with image_dimensions validator", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Image Dimensions", + api_key: "gallery_with_dimensions", + validators: { + image_dimensions: { + width_min_value: 100, + width_max_value: 1920, + height_min_value: 100, + height_max_value: 1080, + }, + }, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with Image Dimensions", + body: { + api_key: "gallery_with_dimensions", + validators: { + image_dimensions: { + width_min_value: 100, + width_max_value: 1920, + height_min_value: 100, + height_max_value: 1080, + }, + }, + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field with image_aspect_ratio validator", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Aspect Ratio", + api_key: "gallery_with_aspect_ratio", + validators: { + image_aspect_ratio: { eq_ar_numerator: 16, eq_ar_denominator: 9 }, + }, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with Aspect Ratio", + body: { + api_key: "gallery_with_aspect_ratio", + validators: { + image_aspect_ratio: { eq_ar_numerator: 16, eq_ar_denominator: 9 }, + }, + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field with extension validator", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Extensions", + api_key: "gallery_with_extensions", + validators: { + extension: { predefined_list: "image" }, + }, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with Extensions", + body: { + api_key: "gallery_with_extensions", + validators: { + extension: { predefined_list: "image" }, + }, + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field with required_alt_title validator", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Alt Title", + api_key: "gallery_with_alt_title", + validators: { + required_alt_title: { title: true, alt: true }, + }, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with Alt Title", + body: { + api_key: "gallery_with_alt_title", + validators: { + required_alt_title: { title: true, alt: true }, + }, + }, + } satisfies AssetGalleryConfig); + }); + + it("can generate a gallery field with multiple validators", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Multiple Validators", + api_key: "gallery_multiple_validators", + validators: { + size: { min: 1, max: 5 }, + file_size: { max_value: 10, max_unit: "MB" }, + extension: { predefined_list: "image" }, + }, + }), + }); + + expect(galleryGenerator.generateBuildConfig()).toEqual({ + label: "Gallery with Multiple Validators", + body: { + api_key: "gallery_multiple_validators", + validators: { + size: { min: 1, max: 5 }, + file_size: { max_value: 10, max_unit: "MB" }, + extension: { predefined_list: "image" }, + }, + }, + } satisfies AssetGalleryConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Test Gallery", + api_key: "test_gallery", + }), + }); + + const methodCall = galleryGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addAssetGallery\(/); + expect(methodCall).toContain('"Test Gallery"'); + expect(methodCall).toContain('"test_gallery"'); + }); + + it("generates method call with validators", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Validators", + api_key: "gallery_with_validators", + validators: { + size: { min: 1, max: 10 }, + }, + }), + }); + + const methodCall = galleryGenerator.generateMethodCall(); + + expect(methodCall).toContain("size"); + expect(methodCall).toMatch(/\.addAssetGallery\(/); + }); + + it("generates method call with hint", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Gallery with Hint", + api_key: "gallery_with_hint", + hint: "Upload images here", + }), + }); + + const methodCall = galleryGenerator.generateMethodCall(); + + expect(methodCall).toContain('"Upload images here"'); + expect(methodCall).toMatch(/\.addAssetGallery\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic Gallery field from DatoCMS", () => { + const apiResponseField: Field = { + id: "gallery-field-123", + type: "field", + label: "Image Gallery", + field_type: "gallery", + api_key: "image_gallery", + hint: "Upload multiple images for the gallery", + localized: false, + validators: { + size: { min: 1, max: 10 }, + file_size: { max_value: 5, max_unit: "MB" }, + extension: { predefined_list: "image" }, + required_alt_title: { title: true, alt: false }, + }, + position: 1, + appearance: { + addons: [], + editor: "gallery", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const galleryGenerator = new GalleryFieldGenerator({ + field: apiResponseField, + }); + + const config = galleryGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Image Gallery", + body: { + api_key: "image_gallery", + hint: "Upload multiple images for the gallery", + validators: { + size: { min: 1, max: 10 }, + file_size: { max_value: 5, max_unit: "MB" }, + extension: { predefined_list: "image" }, + required_alt_title: { title: true, alt: false }, + }, + }, + } satisfies AssetGalleryConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const galleryGenerator = new GalleryFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(galleryGenerator.getMethodCallName()).toBe("addAssetGallery"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/GalleryFieldGenerator.ts b/src/FileGeneration/FieldGenerators/GalleryFieldGenerator.ts new file mode 100644 index 0000000..b70448b --- /dev/null +++ b/src/FileGeneration/FieldGenerators/GalleryFieldGenerator.ts @@ -0,0 +1,87 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addAssetGallery() method calls. + */ +export class GalleryFieldGenerator extends FieldGenerator<"addAssetGallery"> { + getMethodCallName() { + return "addAssetGallery" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addAssetGallery"> { + const config = + this.createBaseConfig() as MethodNameToConfig<"addAssetGallery">; + const body = this.buildGalleryFieldBody(); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildGalleryFieldBody(): NonNullable< + MethodNameToConfig<"addAssetGallery">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addAssetGallery">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addGalleryValidators(body); + + return body; + } + + private addGalleryValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + // Gallery fields support specific validators but not 'required' + this.processGalleryValidators(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + private processGalleryValidators( + validators: NonNullable< + NonNullable["body"]>["validators"] + >, + ): void { + const fieldValidators = this.field.validators; + if (!fieldValidators) return; + + // Handle gallery-specific validators + if (fieldValidators.size) { + (validators as any).size = fieldValidators.size; + } + if (fieldValidators.file_size) { + (validators as any).file_size = fieldValidators.file_size; + } + if (fieldValidators.image_dimensions) { + (validators as any).image_dimensions = fieldValidators.image_dimensions; + } + if (fieldValidators.image_aspect_ratio) { + (validators as any).image_aspect_ratio = + fieldValidators.image_aspect_ratio; + } + if (fieldValidators.extension) { + (validators as any).extension = fieldValidators.extension; + } + if (fieldValidators.required_alt_title) { + (validators as any).required_alt_title = + fieldValidators.required_alt_title; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/IntegerFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/IntegerFieldGenerator.test.ts new file mode 100644 index 0000000..ed18b83 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/IntegerFieldGenerator.test.ts @@ -0,0 +1,311 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { IntegerConfig } from "@/Fields/Integer"; +import { IntegerFieldGenerator } from "@/FileGeneration/FieldGenerators/IntegerFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "integer", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "integer", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("IntegerFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate an integer field with label", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Count", + api_key: "count", + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Count", + body: { + api_key: "count", + }, + } satisfies IntegerConfig); + }); + + it("does not include position", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer without Position", + api_key: "integer_without_position", + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Integer without Position", + body: { + api_key: "integer_without_position", + }, + } satisfies IntegerConfig); + }); + + it("can generate an integer field with a hint", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer with Hint", + api_key: "integer_with_hint", + hint: "Enter a whole number", + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Integer with Hint", + body: { + api_key: "integer_with_hint", + hint: "Enter a whole number", + }, + } satisfies IntegerConfig); + }); + + it("can generate an integer field without a hint", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer without Hint", + api_key: "integer_without_hint", + hint: null, + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Integer without Hint", + body: { + api_key: "integer_without_hint", + }, + } satisfies IntegerConfig); + }); + + it("should not include validators if none are set", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer without Validators", + api_key: "integer_without_validators", + validators: {}, + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Integer without Validators", + body: { + api_key: "integer_without_validators", + }, + } satisfies IntegerConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required integer field", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Required Integer", + api_key: "required_integer", + validators: { required: {} }, + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Required Integer", + body: { + api_key: "required_integer", + validators: { required: true }, + }, + } satisfies IntegerConfig); + }); + + it("can generate a non-required integer field", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Non-Required Integer", + api_key: "non_required_integer", + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Integer", + body: { + api_key: "non_required_integer", + }, + } satisfies IntegerConfig); + }); + }); + + describe("Number range", () => { + it("can generate an integer field with number_range validator", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer with Range", + api_key: "integer_with_range", + validators: { + number_range: { min: 1, max: 100 }, + }, + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Integer with Range", + body: { + api_key: "integer_with_range", + validators: { + number_range: { min: 1, max: 100 }, + }, + }, + } satisfies IntegerConfig); + }); + + it("can generate an integer field with multiple validators", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer with Multiple Validators", + api_key: "integer_multiple_validators", + validators: { + required: {}, + number_range: { min: 0, max: 999 }, + }, + }), + }); + + expect(integerGenerator.generateBuildConfig()).toEqual({ + label: "Integer with Multiple Validators", + body: { + api_key: "integer_multiple_validators", + validators: { + required: true, + number_range: { min: 0, max: 999 }, + }, + }, + } satisfies IntegerConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Test Integer", + api_key: "test_integer", + }), + }); + + const methodCall = integerGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addInteger\(/); + expect(methodCall).toContain('"Test Integer"'); + expect(methodCall).toContain('"test_integer"'); + }); + + it("generates method call with validators", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer with Validators", + api_key: "integer_with_validators", + validators: { + number_range: { min: 1, max: 10 }, + }, + }), + }); + + const methodCall = integerGenerator.generateMethodCall(); + + expect(methodCall).toContain("number_range"); + expect(methodCall).toMatch(/\.addInteger\(/); + }); + + it("generates method call with hint", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Integer with Hint", + api_key: "integer_with_hint", + hint: "Enter whole number", + }), + }); + + const methodCall = integerGenerator.generateMethodCall(); + + expect(methodCall).toContain('"Enter whole number"'); + expect(methodCall).toMatch(/\.addInteger\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic Integer field from DatoCMS", () => { + const apiResponseField: Field = { + id: "integer-field-123", + type: "field", + label: "Quantity", + field_type: "integer", + api_key: "quantity", + hint: "Enter quantity in stock", + localized: false, + validators: { + required: {}, + number_range: { min: 0, max: 10000 }, + }, + position: 1, + appearance: { + addons: [], + editor: "integer", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const integerGenerator = new IntegerFieldGenerator({ + field: apiResponseField, + }); + + const config = integerGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Quantity", + body: { + api_key: "quantity", + hint: "Enter quantity in stock", + validators: { + required: true, + number_range: { min: 0, max: 10000 }, + }, + }, + } satisfies IntegerConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const integerGenerator = new IntegerFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(integerGenerator.getMethodCallName()).toBe("addInteger"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/IntegerFieldGenerator.ts b/src/FileGeneration/FieldGenerators/IntegerFieldGenerator.ts new file mode 100644 index 0000000..07ddf11 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/IntegerFieldGenerator.ts @@ -0,0 +1,10 @@ +import { NumberFieldGenerator } from "@/FileGeneration/FieldGenerators/NumberFieldGenerator"; + +/** + * Generates ItemTypeBuilder.addInteger() method calls. + */ +export class IntegerFieldGenerator extends NumberFieldGenerator<"addInteger"> { + getMethodCallName() { + return "addInteger" as const; + } +} diff --git a/src/FileGeneration/FieldGenerators/JsonFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/JsonFieldGenerator.test.ts new file mode 100644 index 0000000..4477d36 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/JsonFieldGenerator.test.ts @@ -0,0 +1,130 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import { JsonFieldGenerator } from "./JsonFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "json", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "json", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("JsonFieldGenerator", () => { + describe("getMethodCallName", () => { + it("should return addJson for no editor", () => { + const field = createMockField({ + api_key: "json_field", + label: "JSON Field", + }); + + const generator = new JsonFieldGenerator({ field }); + expect(generator.getMethodCallName()).toBe("addJson"); + }); + + it("should return addJson for unknown editor", () => { + const field = createMockField({ + api_key: "json_field", + label: "JSON Field", + appearance: { + editor: "unknown_editor", + parameters: {}, + addons: [], + }, + }); + + const generator = new JsonFieldGenerator({ field }); + expect(generator.getMethodCallName()).toBe("addJson"); + }); + }); + + describe("generateBuildConfig", () => { + it("should generate basic JSON config", () => { + const field = createMockField({ + api_key: "json_field", + label: "JSON Field", + }); + + const generator = new JsonFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "JSON Field", + }); + }); + + it("should include body with validators when required", () => { + const field = createMockField({ + api_key: "required_json", + label: "Required JSON", + validators: { + required: {}, + }, + }); + + const generator = new JsonFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Required JSON", + body: { + api_key: "required_json", + validators: { + required: true, + }, + }, + }); + }); + + it("should include hint and default value in body", () => { + const field = createMockField({ + api_key: "json_field", + label: "JSON Field", + hint: "Store JSON data", + default_value: { key: "value" }, + }); + + const generator = new JsonFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "JSON Field", + body: { + api_key: "json_field", + hint: "Store JSON data", + default_value: { key: "value" }, + }, + }); + }); + }); + + describe("generateMethodCall", () => { + it("should generate correct method call for JSON field", () => { + const field = createMockField({ + api_key: "json_field", + label: "JSON Field", + hint: "Store JSON data", + }); + + const generator = new JsonFieldGenerator({ field }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toBe( + `.addJson({ label: "JSON Field", body: { api_key: "json_field", hint: "Store JSON data" } })`, + ); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/JsonFieldGenerator.ts b/src/FileGeneration/FieldGenerators/JsonFieldGenerator.ts new file mode 100644 index 0000000..bea49fc --- /dev/null +++ b/src/FileGeneration/FieldGenerators/JsonFieldGenerator.ts @@ -0,0 +1,41 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +/** + * Generates ItemTypeBuilder method calls for JSON field types. + * Handles plain JSON fields without specific editors. + */ +export class JsonFieldGenerator extends FieldGenerator<"addJson"> { + getMethodCallName() { + return "addJson" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addJson"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody() as any; + + // Add validators if any exist + if (this.hasValidators()) { + const validators: NonNullable< + NonNullable["body"]>["validators"] + > = {}; + + this.processRequiredValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + // Add hint and default value + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + // Only include body if it has content beyond api_key + if (this.hasBodyContent(body) && Object.keys(body).length > 1) { + return { ...config, body }; + } + + return config; + } +} diff --git a/src/FileGeneration/FieldGenerators/LinkFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/LinkFieldGenerator.test.ts new file mode 100644 index 0000000..d654198 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/LinkFieldGenerator.test.ts @@ -0,0 +1,312 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import { LinkFieldGenerator } from "@/FileGeneration/FieldGenerators/LinkFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "link", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "link_select", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +function createMockItemType( + name: string, + modular_block: boolean = false, +): ItemType { + return { + id: `test-${name.toLowerCase()}-id`, + type: "item_type", + name, + api_key: name.toLowerCase().replace(/\s+/g, "_"), + collection_appearance: "table", + singleton: false, + all_locales_required: true, + sortable: false, + modular_block, + draft_mode_active: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_meta: null, + has_singleton_item: false, + hint: null, + inverse_relationships_enabled: false, + singleton_item: null, + fields: [], + fieldsets: [], + presentation_title_field: null, + presentation_image_field: null, + title_field: null, + image_preview_field: null, + excerpt_field: null, + ordering_field: null, + workflow: null, + meta: { has_singleton_item: false }, + } as ItemType; +} + +describe("LinkFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a basic link field", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Related Model", + api_key: "related_model", + }), + }); + + const config = linkGenerator.generateBuildConfig(); + expect(config.label).toBe("Related Model"); + expect(config.appearance).toBe("compact"); + expect(config.body?.api_key).toBe("related_model"); + expect(config.body?.validators).toBeUndefined(); + }); + + it("maps link_select editor to compact appearance", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Compact Link", + api_key: "compact_link", + appearance: { addons: [], editor: "link_select", parameters: {} }, + }), + }); + + const config = linkGenerator.generateBuildConfig(); + expect(config.appearance).toBe("compact"); + }); + + it("maps link_embed editor to expanded appearance", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Expanded Link", + api_key: "expanded_link", + appearance: { addons: [], editor: "link_embed", parameters: {} }, + }), + }); + + const config = linkGenerator.generateBuildConfig(); + expect(config.appearance).toBe("expanded"); + }); + + it("defaults to compact appearance for unknown editor", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Unknown Link", + api_key: "unknown_link", + appearance: { addons: [], editor: "unknown_editor", parameters: {} }, + }), + }); + + const config = linkGenerator.generateBuildConfig(); + expect(config.appearance).toBe("compact"); + }); + }); + + describe("Validators", () => { + it("handles item_item_type validator with getModel calls", () => { + const itemTypeReferences = new Map([ + ["terminal-id", createMockItemType("Terminal")], + ["airline-id", createMockItemType("Airline")], + ]); + + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Model Link", + api_key: "model_link", + validators: { + item_item_type: { + item_types: ["terminal-id", "airline-id"], + }, + }, + }), + itemTypeReferences, + }); + + const methodCall = linkGenerator.generateMethodCall(); + expect(methodCall).toContain('await getModel("Terminal")'); + expect(methodCall).toContain('await getModel("Airline")'); + }); + + it("handles item_item_type validator with getBlock calls", () => { + const itemTypeReferences = new Map([ + ["block-id", createMockItemType("TestBlock", true)], + ]); + + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Block Link", + api_key: "block_link", + validators: { + item_item_type: { + item_types: ["block-id"], + }, + }, + }), + itemTypeReferences, + }); + + const methodCall = linkGenerator.generateMethodCall(); + expect(methodCall).toContain('await getBlock("TestBlock")'); + }); + + it("handles complex model names with PascalCase", () => { + const itemTypeReferences = new Map([ + ["foobar-id", createMockItemType("Foo Bar")], + ]); + + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Complex Name Link", + api_key: "complex_name_link", + validators: { + item_item_type: { + item_types: ["foobar-id"], + }, + }, + }), + itemTypeReferences, + }); + + const methodCall = linkGenerator.generateMethodCall(); + expect(methodCall).toContain('await getModel("FooBar")'); + }); + + it("handles required validator", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Required Link", + api_key: "required_link", + validators: { + required: {}, + }, + }), + }); + + const config = linkGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + }); + + it("handles unique validator", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Unique Link", + api_key: "unique_link", + validators: { + unique: {}, + }, + }), + }); + + const config = linkGenerator.generateBuildConfig(); + expect(config.body?.validators?.unique).toBe(true); + }); + + it("includes hint when present", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Link with Hint", + api_key: "link_with_hint", + hint: "Select a related model", + }), + }); + + const config = linkGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("Select a related model"); + }); + }); + + describe("Error handling", () => { + it("throws error when item type reference is missing", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Missing Reference", + api_key: "missing_reference", + validators: { + item_item_type: { + item_types: ["nonexistent-id"], + }, + }, + }), + itemTypeReferences: new Map(), + }); + + expect(() => linkGenerator.generateMethodCall()).toThrow( + 'Field missing_reference (link): Item type with ID "nonexistent-id" not found in references', + ); + }); + + it("throws error when itemTypeReferences is not provided", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "No References", + api_key: "no_references", + validators: { + item_item_type: { + item_types: ["some-id"], + }, + }, + }), + }); + + expect(() => linkGenerator.generateMethodCall()).toThrow( + "Field no_references (link): Cannot resolve item type references - references not available", + ); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call syntax", () => { + const itemTypeReferences = new Map([ + ["terminal-id", createMockItemType("Terminal")], + ]); + + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Test Link", + api_key: "test_link", + validators: { + item_item_type: { + item_types: ["terminal-id"], + }, + }, + }), + itemTypeReferences, + }); + + const methodCall = linkGenerator.generateMethodCall(); + expect(methodCall).toMatch(/^\.addLink\(/); + expect(methodCall).toContain('"Test Link"'); + expect(methodCall).toContain('api_key: "test_link"'); + expect(methodCall).toContain('await getModel("Terminal")'); + }); + + it("generates method call without validators when none present", () => { + const linkGenerator = new LinkFieldGenerator({ + field: createMockField({ + label: "Simple Link", + api_key: "simple_link", + }), + }); + + const methodCall = linkGenerator.generateMethodCall(); + expect(methodCall).not.toContain("validators"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/LinkFieldGenerator.ts b/src/FileGeneration/FieldGenerators/LinkFieldGenerator.ts new file mode 100644 index 0000000..94f610e --- /dev/null +++ b/src/FileGeneration/FieldGenerators/LinkFieldGenerator.ts @@ -0,0 +1,94 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addLink() method calls for single link fields. + * Link fields reference other item types (blocks or models) in DatoCMS. + */ +export class LinkFieldGenerator extends FieldGenerator<"addLink"> { + getMethodCallName() { + return "addLink" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addLink"> { + const config = this.createBaseConfig() as MethodNameToConfig<"addLink">; + + // Add appearance if specified + if (this.field.appearance?.editor) { + config.appearance = this.mapEditorToAppearance( + this.field.appearance.editor, + ); + } + + const body = this.buildLinkFieldBody(); + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildLinkFieldBody(): NonNullable< + MethodNameToConfig<"addLink">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addLink">["body"] + >; + + this.addHintToBody(body); + this.addLinkValidators(body); + + return body; + } + + private addLinkValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + // Add required item_item_type validator + if (this.field.validators?.item_item_type) { + const itemItemType = { + ...this.field.validators.item_item_type, + } as Record; + + // Convert item_types from IDs to getModel/getBlock calls + if (Array.isArray(itemItemType.item_types)) { + const getCalls = this.convertItemTypeIdsToGetCalls( + itemItemType.item_types as string[], + ); + itemItemType.item_types = getCalls; + } + + this.addOptionalProperty(validators, "item_item_type", itemItemType); + } + + // Add optional validators + this.processRequiredValidator(validators); + this.processUniqueValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + /** + * Map DatoCMS editor to ItemTypeBuilder appearance + */ + private mapEditorToAppearance(editor: string): "compact" | "expanded" { + switch (editor) { + case "link_select": + return "compact"; + case "link_embed": + return "expanded"; + default: + return "compact"; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/LinksFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/LinksFieldGenerator.test.ts new file mode 100644 index 0000000..66a5a8d --- /dev/null +++ b/src/FileGeneration/FieldGenerators/LinksFieldGenerator.test.ts @@ -0,0 +1,396 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import { LinksFieldGenerator } from "@/FileGeneration/FieldGenerators/LinksFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "links", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "links_select", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +function createMockItemType( + name: string, + modular_block: boolean = false, +): ItemType { + return { + id: `test-${name.toLowerCase()}-id`, + type: "item_type", + name, + api_key: name.toLowerCase().replace(/\s+/g, "_"), + collection_appearance: "table", + singleton: false, + all_locales_required: true, + sortable: false, + modular_block, + draft_mode_active: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_meta: null, + has_singleton_item: false, + hint: null, + inverse_relationships_enabled: false, + singleton_item: null, + fields: [], + fieldsets: [], + presentation_title_field: null, + presentation_image_field: null, + title_field: null, + image_preview_field: null, + excerpt_field: null, + ordering_field: null, + workflow: null, + meta: { has_singleton_item: false }, + } as ItemType; +} + +describe("LinksFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a basic links field", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Related Models", + api_key: "related_models", + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.label).toBe("Related Models"); + expect(config.appearance).toBe("compact"); + expect(config.body?.api_key).toBe("related_models"); + expect(config.body?.validators).toBeUndefined(); + }); + + it("maps links_select editor to compact appearance", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Compact Links", + api_key: "compact_links", + appearance: { addons: [], editor: "links_select", parameters: {} }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.appearance).toBe("compact"); + }); + + it("maps links_embed editor to expanded appearance", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Expanded Links", + api_key: "expanded_links", + appearance: { addons: [], editor: "links_embed", parameters: {} }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.appearance).toBe("expanded"); + }); + + it("defaults to compact appearance for unknown editor", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Unknown Links", + api_key: "unknown_links", + appearance: { addons: [], editor: "unknown_editor", parameters: {} }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.appearance).toBe("compact"); + }); + }); + + describe("Validators", () => { + it("handles items_item_type validator with multiple getModel calls", () => { + const itemTypeReferences = new Map([ + ["terminal-id", createMockItemType("Terminal")], + ["airline-id", createMockItemType("Airline")], + ["foobar-id", createMockItemType("Foo Bar")], + ]); + + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Multiple Models", + api_key: "multiple_models", + validators: { + items_item_type: { + item_types: ["terminal-id", "airline-id", "foobar-id"], + }, + }, + }), + itemTypeReferences, + }); + + const methodCall = linksGenerator.generateMethodCall(); + expect(methodCall).toContain('await getModel("Terminal")'); + expect(methodCall).toContain('await getModel("Airline")'); + expect(methodCall).toContain('await getModel("FooBar")'); + }); + + it("handles items_item_type validator with mixed blocks and models", () => { + const itemTypeReferences = new Map([ + ["model-id", createMockItemType("TestModel")], + ["block-id", createMockItemType("TestBlock", true)], + ]); + + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Mixed Links", + api_key: "mixed_links", + validators: { + items_item_type: { + item_types: ["model-id", "block-id"], + }, + }, + }), + itemTypeReferences, + }); + + const methodCall = linksGenerator.generateMethodCall(); + expect(methodCall).toContain('await getModel("TestModel")'); + expect(methodCall).toContain('await getBlock("TestBlock")'); + }); + + it("handles size validator with min and max", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Size Constrained", + api_key: "size_constrained", + validators: { + size: { + min: 2, + max: 5, + }, + }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.body?.validators?.size).toEqual({ + min: 2, + max: 5, + }); + }); + + it("handles size validator with only min", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Min Size", + api_key: "min_size", + validators: { + size: { + min: 1, + }, + }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.body?.validators?.size).toEqual({ + min: 1, + }); + }); + + it("handles size validator with only max", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Max Size", + api_key: "max_size", + validators: { + size: { + max: 10, + }, + }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.body?.validators?.size).toEqual({ + max: 10, + }); + }); + + it("handles required validator", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Required Links", + api_key: "required_links", + validators: { + required: {}, + }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + // biome-ignore lint/suspicious/noExplicitAny: Type casting required for test assertions on dynamic validator structures + expect((config.body?.validators as any)?.required).toBe(true); + }); + + it("handles unique validator", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Unique Links", + api_key: "unique_links", + validators: { + unique: {}, + }, + }), + }); + + const config = linksGenerator.generateBuildConfig(); + // biome-ignore lint/suspicious/noExplicitAny: Type casting required for test assertions on dynamic validator structures + expect((config.body?.validators as any)?.unique).toBe(true); + }); + + it("includes hint when present", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Links with Hint", + api_key: "links_with_hint", + hint: "Select multiple related models", + }), + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("Select multiple related models"); + }); + + it("handles combined validators", () => { + const itemTypeReferences = new Map([ + ["terminal-id", createMockItemType("Terminal")], + ]); + + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Complex Links", + api_key: "complex_links", + validators: { + items_item_type: { + item_types: ["terminal-id"], + }, + size: { + min: 1, + max: 3, + }, + required: {}, + unique: {}, + }, + }), + itemTypeReferences, + }); + + const config = linksGenerator.generateBuildConfig(); + expect(config.body?.validators?.items_item_type).toBeDefined(); + expect(config.body?.validators?.size).toEqual({ min: 1, max: 3 }); + // biome-ignore lint/suspicious/noExplicitAny: Type casting required for test assertions on dynamic validator structures + expect((config.body?.validators as any)?.required).toBe(true); + // biome-ignore lint/suspicious/noExplicitAny: Type casting required for test assertions on dynamic validator structures + expect((config.body?.validators as any)?.unique).toBe(true); + }); + }); + + describe("Error handling", () => { + it("throws error when item type reference is missing", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Missing Reference", + api_key: "missing_reference", + validators: { + items_item_type: { + item_types: ["nonexistent-id"], + }, + }, + }), + itemTypeReferences: new Map(), + }); + + expect(() => linksGenerator.generateMethodCall()).toThrow( + 'Field missing_reference (links): Item type with ID "nonexistent-id" not found in references', + ); + }); + + it("throws error when itemTypeReferences is not provided", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "No References", + api_key: "no_references", + validators: { + items_item_type: { + item_types: ["some-id"], + }, + }, + }), + }); + + expect(() => linksGenerator.generateMethodCall()).toThrow( + "Field no_references (links): Cannot resolve item type references - references not available", + ); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call syntax", () => { + const itemTypeReferences = new Map([ + ["terminal-id", createMockItemType("Terminal")], + ["airline-id", createMockItemType("Airline")], + ]); + + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Test Links", + api_key: "test_links", + validators: { + items_item_type: { + item_types: ["terminal-id", "airline-id"], + }, + size: { + min: 1, + max: 5, + }, + }, + }), + itemTypeReferences, + }); + + const methodCall = linksGenerator.generateMethodCall(); + expect(methodCall).toMatch(/^\.addLinks\(/); + expect(methodCall).toContain('"Test Links"'); + expect(methodCall).toContain('api_key: "test_links"'); + expect(methodCall).toContain('await getModel("Terminal")'); + expect(methodCall).toContain('await getModel("Airline")'); + expect(methodCall).toContain("min: 1"); + expect(methodCall).toContain("max: 5"); + }); + + it("generates method call without validators when none present", () => { + const linksGenerator = new LinksFieldGenerator({ + field: createMockField({ + label: "Simple Links", + api_key: "simple_links", + }), + }); + + const methodCall = linksGenerator.generateMethodCall(); + expect(methodCall).not.toContain("validators"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/LinksFieldGenerator.ts b/src/FileGeneration/FieldGenerators/LinksFieldGenerator.ts new file mode 100644 index 0000000..cbd0ba2 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/LinksFieldGenerator.ts @@ -0,0 +1,96 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addLinks() method calls for multiple links fields. + * Links fields allow selection of multiple item types (blocks or models) in DatoCMS. + */ +export class LinksFieldGenerator extends FieldGenerator<"addLinks"> { + getMethodCallName() { + return "addLinks" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addLinks"> { + const config = this.createBaseConfig() as MethodNameToConfig<"addLinks">; + + // Add appearance if specified + if (this.field.appearance?.editor) { + config.appearance = this.mapEditorToAppearance( + this.field.appearance.editor, + ); + } + + const body = this.buildLinksFieldBody(); + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildLinksFieldBody(): NonNullable< + MethodNameToConfig<"addLinks">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addLinks">["body"] + >; + + this.addHintToBody(body); + this.addLinksValidators(body); + + return body; + } + + private addLinksValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as Record; + + // Add required items_item_type validator + if (this.field.validators?.items_item_type) { + const itemsItemType = { + ...this.field.validators.items_item_type, + } as Record; + + // Convert item_types from IDs to getModel/getBlock calls + if (Array.isArray(itemsItemType.item_types)) { + const getCalls = this.convertItemTypeIdsToGetCalls( + itemsItemType.item_types as string[], + ); + itemsItemType.item_types = getCalls; + } + + this.addOptionalProperty(validators, "items_item_type", itemsItemType); + } + + // Add optional validators + this.processRequiredValidator(validators); + this.processUniqueValidator(validators); + + if (this.field.validators?.size) { + this.addOptionalProperty(validators, "size", this.field.validators.size); + } + + if (Object.keys(validators).length > 0) { + this.addOptionalProperty(body, "validators", validators); + } + } + + /** + * Map DatoCMS editor to ItemTypeBuilder appearance + */ + private mapEditorToAppearance(editor: string): "compact" | "expanded" { + switch (editor) { + case "links_select": + return "compact"; + case "links_embed": + return "expanded"; + default: + return "compact"; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/LocationFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/LocationFieldGenerator.test.ts new file mode 100644 index 0000000..18eb289 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/LocationFieldGenerator.test.ts @@ -0,0 +1,303 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { LocationConfig } from "@/Fields/Location"; +import { LocationFieldGenerator } from "@/FileGeneration/FieldGenerators/LocationFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "lat_lon", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "map", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("LocationFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a location with label", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location", + api_key: "location", + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Location", + body: { + api_key: "location", + }, + } satisfies LocationConfig); + }); + + it("does not include position", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location without Position", + api_key: "location_without_position", + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Location without Position", + body: { + api_key: "location_without_position", + }, + } satisfies LocationConfig); + }); + + it("can generate a location with a hint", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location with Hint", + api_key: "location_with_hint", + hint: "Please enter the exact coordinates", + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Location with Hint", + body: { + api_key: "location_with_hint", + hint: "Please enter the exact coordinates", + }, + } satisfies LocationConfig); + }); + + it("can generate a location without a hint", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location without Hint", + api_key: "location_without_hint", + hint: null, + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Location without Hint", + body: { + api_key: "location_without_hint", + }, + } satisfies LocationConfig); + }); + + it("can generate a location with a default value", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location with Default Value", + api_key: "location_with_default_value", + default_value: { latitude: 40.7128, longitude: -74.006 }, + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Location with Default Value", + body: { + api_key: "location_with_default_value", + default_value: { latitude: 40.7128, longitude: -74.006 }, + }, + } satisfies LocationConfig); + }); + + it("can generate a location without a default value", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location without Default Value", + api_key: "location_without_default_value", + default_value: null, + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Location without Default Value", + body: { + api_key: "location_without_default_value", + }, + } satisfies LocationConfig); + }); + + it("should not include validators if none are set", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location without Validators", + api_key: "location_without_validators", + validators: {}, + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Location without Validators", + body: { + api_key: "location_without_validators", + }, + } satisfies LocationConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required location field", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Required Location", + api_key: "required-location-api-key", + validators: { required: {} }, + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Required Location", + body: { + api_key: "required-location-api-key", + validators: { required: true }, + }, + } satisfies LocationConfig); + }); + + it("can generate a non-required location field", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Non-Required Location", + api_key: "non-required-location-api-key", + }), + }); + + expect(locationGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Location", + body: { + api_key: "non-required-location-api-key", + }, + } satisfies LocationConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Test Location", + api_key: "test_location", + }), + }); + + const methodCall = locationGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addLocation\(/); + expect(methodCall).toContain('"Test Location"'); + expect(methodCall).toContain('"test_location"'); + }); + + it("generates method call with required validator", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Required Location", + api_key: "required_location", + validators: { required: {} }, + }), + }); + + const methodCall = locationGenerator.generateMethodCall(); + + expect(methodCall).toContain("required: true"); + expect(methodCall).toMatch(/\.addLocation\(/); + }); + + it("generates method call with hint", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location with Hint", + api_key: "location_with_hint", + hint: "Enter coordinates", + }), + }); + + const methodCall = locationGenerator.generateMethodCall(); + + expect(methodCall).toContain('"Enter coordinates"'); + expect(methodCall).toMatch(/\.addLocation\(/); + }); + + it("generates method call with default value", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Location with Default", + api_key: "location_with_default", + default_value: { latitude: 51.5074, longitude: -0.1278 }, + }), + }); + + const methodCall = locationGenerator.generateMethodCall(); + + expect(methodCall).toContain("latitude: 51.5074"); + expect(methodCall).toContain("longitude: -0.1278"); + expect(methodCall).toMatch(/\.addLocation\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic Location field from DatoCMS", () => { + const apiResponseField: Field = { + id: "location-field-123", + type: "field", + label: "Office Location", + field_type: "lat_lon", + api_key: "office_location", + hint: "Please select the office location on the map", + localized: false, + validators: { + required: {}, + }, + position: 3, + appearance: { addons: [], editor: "map", parameters: {} }, + default_value: { latitude: 37.7749, longitude: -122.4194 }, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const locationGenerator = new LocationFieldGenerator({ + field: apiResponseField, + }); + + const config = locationGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Office Location", + body: { + api_key: "office_location", + hint: "Please select the office location on the map", + default_value: { latitude: 37.7749, longitude: -122.4194 }, + validators: { + required: true, + }, + }, + } satisfies LocationConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const locationGenerator = new LocationFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(locationGenerator.getMethodCallName()).toBe("addLocation"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/LocationFieldGenerator.ts b/src/FileGeneration/FieldGenerators/LocationFieldGenerator.ts new file mode 100644 index 0000000..bca0a44 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/LocationFieldGenerator.ts @@ -0,0 +1,54 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addLocation() method calls. + */ +export class LocationFieldGenerator extends FieldGenerator<"addLocation"> { + getMethodCallName() { + return "addLocation" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addLocation"> { + const config = this.createBaseConfig() as MethodNameToConfig<"addLocation">; + const body = this.buildLocationFieldBody(); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildLocationFieldBody(): NonNullable< + MethodNameToConfig<"addLocation">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addLocation">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addLocationValidators(body); + + return body; + } + + private addLocationValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + this.processRequiredValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/MarkdownFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/MarkdownFieldGenerator.test.ts new file mode 100644 index 0000000..1c0882e --- /dev/null +++ b/src/FileGeneration/FieldGenerators/MarkdownFieldGenerator.test.ts @@ -0,0 +1,218 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { MarkdownConfig } from "@/Fields/Markdown"; +import { MarkdownFieldGenerator } from "@/FileGeneration/FieldGenerators/MarkdownFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "text", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "markdown", + parameters: { toolbar: ["heading", "bold", "italic"] }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("MarkdownFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a markdown field with label and toolbar", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Markdown Content", + api_key: "markdown_content", + }), + }); + + expect(markdownGenerator.generateBuildConfig()).toEqual({ + label: "Markdown Content", + toolbar: ["heading", "bold", "italic"], + body: { + api_key: "markdown_content", + }, + } satisfies MarkdownConfig); + }); + + it("returns correct method call name", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(markdownGenerator.getMethodCallName()).toBe("addMarkdown"); + }); + + it("includes hint when present", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Markdown with Hint", + api_key: "markdown_with_hint", + hint: "This is a hint", + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a hint"); + }); + + it("includes default_value when present", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Markdown with Default", + api_key: "markdown_with_default", + default_value: "# Default Content", + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("# Default Content"); + }); + + it("handles empty toolbar", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Markdown Empty Toolbar", + api_key: "markdown_empty_toolbar", + appearance: { + addons: [], + editor: "markdown", + parameters: { toolbar: [] }, + }, + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.toolbar).toEqual([]); + }); + + it("handles missing toolbar parameter", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Markdown No Toolbar", + api_key: "markdown_no_toolbar", + appearance: { + addons: [], + editor: "markdown", + parameters: {}, + }, + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.toolbar).toEqual([]); + }); + }); + + describe("Validators", () => { + it("includes required validator", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Required Markdown", + api_key: "required_markdown", + validators: { required: {} }, + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + }); + + it("includes length validator", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Length Markdown", + api_key: "length_markdown", + validators: { length: { min: 10, max: 100 } }, + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.body?.validators?.length).toEqual({ min: 10, max: 100 }); + }); + + it("includes format validator", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Format Markdown", + api_key: "format_markdown", + validators: { format: { predefined_pattern: "email" } }, + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.body?.validators?.format).toEqual({ + predefined_pattern: "email", + }); + }); + + it("includes sanitized_html validator", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Sanitized Markdown", + api_key: "sanitized_markdown", + validators: { sanitized_html: {} }, + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.body?.validators?.sanitized_html).toEqual({ + sanitize_before_validation: true, + }); + }); + + it("combines multiple validators", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Multi Validator Markdown", + api_key: "multi_validator_markdown", + validators: { + required: {}, + length: { min: 5, max: 50 }, + sanitized_html: {}, + }, + }), + }); + + const config = markdownGenerator.generateBuildConfig(); + expect(config.body?.validators).toEqual({ + required: true, + length: { min: 5, max: 50 }, + sanitized_html: { sanitize_before_validation: true }, + }); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call string", () => { + const markdownGenerator = new MarkdownFieldGenerator({ + field: createMockField({ + label: "Test Markdown", + api_key: "test_markdown", + hint: "Test hint", + }), + }); + + const methodCall = markdownGenerator.generateMethodCall(); + expect(methodCall).toContain(".addMarkdown("); + expect(methodCall).toContain('label: "Test Markdown"'); + expect(methodCall).toContain('api_key: "test_markdown"'); + expect(methodCall).toContain('hint: "Test hint"'); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/MarkdownFieldGenerator.ts b/src/FileGeneration/FieldGenerators/MarkdownFieldGenerator.ts new file mode 100644 index 0000000..3b6e7e0 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/MarkdownFieldGenerator.ts @@ -0,0 +1,71 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +export class MarkdownFieldGenerator extends FieldGenerator<"addMarkdown"> { + getMethodCallName() { + return "addMarkdown" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addMarkdown"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody(); + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + const validators = this.createTextValidators(); + if (Object.keys(validators).length > 0) { + this.addOptionalProperty(body, "validators", validators); + } + + const toolbar = this.extractToolbarFromParameters() as Array< + | "heading" + | "bold" + | "italic" + | "strikethrough" + | "code" + | "unordered_list" + | "ordered_list" + | "quote" + | "link" + | "image" + | "fullscreen" + >; + + return { + ...config, + toolbar, + ...(this.hasBodyContent(body) && { body }), + }; + } + + private createTextValidators() { + const validators: { + required?: boolean; + length?: { min?: number; max?: number }; + format?: { predefined_pattern?: string; custom_pattern?: RegExp }; + sanitized_html?: { sanitize_before_validation: boolean }; + } = {}; + + // Use the proper validator processing methods + this.processRequiredValidator(validators); + this.processLengthValidator(validators); + this.processFormatValidator(validators); + + if (this.field.validators?.sanitized_html) { + validators.sanitized_html = { + sanitize_before_validation: true, + }; + } + + return validators; + } + + private extractToolbarFromParameters(): string[] { + const toolbar = this.field.appearance?.parameters?.toolbar; + if (Array.isArray(toolbar)) { + return toolbar; + } + return []; + } +} diff --git a/src/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator.test.ts new file mode 100644 index 0000000..d55a1ce --- /dev/null +++ b/src/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator.test.ts @@ -0,0 +1,245 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { MultiLineTextConfig } from "@/Fields/MultiLineText"; +import { MultiLineTextFieldGenerator } from "@/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "text", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, // No appearance for plain multi-line text + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("MultiLineTextFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a multi-line text field with label", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Long Description", + api_key: "long_description", + }), + }); + + expect(multiLineTextGenerator.generateBuildConfig()).toEqual({ + label: "Long Description", + body: { + api_key: "long_description", + }, + } satisfies MultiLineTextConfig); + }); + + it("returns correct method call name", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(multiLineTextGenerator.getMethodCallName()).toBe( + "addMultiLineText", + ); + }); + + it("includes hint when present", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "MultiLineText with Hint", + api_key: "multi_line_text_with_hint", + hint: "This is a hint", + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a hint"); + }); + + it("includes default_value when present", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "MultiLineText with Default", + api_key: "multi_line_text_with_default", + default_value: "Default multi-line\ntext content", + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe( + "Default multi-line\ntext content", + ); + }); + + it("handles minimal configuration", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Minimal MultiLineText", + api_key: "minimal_multi_line_text", + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config).toEqual({ + label: "Minimal MultiLineText", + body: { + api_key: "minimal_multi_line_text", + }, + }); + }); + + it("handles field with empty appearance", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Empty Appearance", + api_key: "empty_appearance", + appearance: { addons: [], editor: "plain", parameters: {} } as any, + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.label).toBe("Empty Appearance"); + expect(config.body?.api_key).toBe("empty_appearance"); + }); + }); + + describe("Validators", () => { + it("includes required validator", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Required MultiLineText", + api_key: "required_multi_line_text", + validators: { required: {} }, + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + }); + + it("includes length validator", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Length MultiLineText", + api_key: "length_multi_line_text", + validators: { length: { min: 10, max: 1000 } }, + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.validators?.length).toEqual({ min: 10, max: 1000 }); + }); + + it("includes format validator", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Format MultiLineText", + api_key: "format_multi_line_text", + validators: { format: { custom_pattern: "^[A-Za-z0-9\\s]+$" } }, + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.validators?.format).toEqual({ + custom_pattern: /^[A-Za-z0-9\s]+$/, + }); + }); + + it("includes sanitized_html validator", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Sanitized MultiLineText", + api_key: "sanitized_multi_line_text", + validators: { sanitized_html: {} }, + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.validators?.sanitized_html).toEqual({ + sanitize_before_validation: true, + }); + }); + + it("combines multiple validators", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Multi Validator MultiLineText", + api_key: "multi_validator_multi_line_text", + validators: { + required: {}, + length: { min: 20, max: 500 }, + format: { predefined_pattern: "no_html" }, + sanitized_html: {}, + }, + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.validators).toEqual({ + required: true, + length: { min: 20, max: 500 }, + format: { predefined_pattern: "no_html" }, + sanitized_html: { sanitize_before_validation: true }, + }); + }); + + it("handles partial length validator", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Min Only MultiLineText", + api_key: "min_only_multi_line_text", + validators: { length: { min: 15 } }, + }), + }); + + const config = multiLineTextGenerator.generateBuildConfig(); + expect(config.body?.validators?.length).toEqual({ min: 15 }); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call string", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Test MultiLineText", + api_key: "test_multi_line_text", + hint: "Test hint", + }), + }); + + const methodCall = multiLineTextGenerator.generateMethodCall(); + expect(methodCall).toContain(".addMultiLineText("); + expect(methodCall).toContain('label: "Test MultiLineText"'); + expect(methodCall).toContain('api_key: "test_multi_line_text"'); + expect(methodCall).toContain('hint: "Test hint"'); + }); + + it("generates minimal method call", () => { + const multiLineTextGenerator = new MultiLineTextFieldGenerator({ + field: createMockField({ + label: "Simple", + api_key: "simple", + }), + }); + + const methodCall = multiLineTextGenerator.generateMethodCall(); + expect(methodCall).toContain(".addMultiLineText("); + expect(methodCall).toContain('label: "Simple"'); + expect(methodCall).toContain('api_key: "simple"'); + expect(methodCall).not.toContain("hint"); + expect(methodCall).not.toContain("validators"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator.ts b/src/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator.ts new file mode 100644 index 0000000..7c10a08 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/MultiLineTextFieldGenerator.ts @@ -0,0 +1,47 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +export class MultiLineTextFieldGenerator extends FieldGenerator<"addMultiLineText"> { + getMethodCallName() { + return "addMultiLineText" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addMultiLineText"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody(); + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + const validators = this.createTextValidators(); + if (Object.keys(validators).length > 0) { + (body as any).validators = validators; + } + + return { + ...config, + ...(this.hasBodyContent(body) && { body }), + }; + } + + private createTextValidators() { + const validators: { + required?: boolean; + length?: { min?: number; max?: number }; + format?: { predefined_pattern?: string; custom_pattern?: RegExp }; + sanitized_html?: { sanitize_before_validation: boolean }; + } = {}; + + this.processRequiredValidator(validators); + this.processLengthValidator(validators); + this.processFormatValidator(validators); + + if (this.field.validators?.sanitized_html) { + validators.sanitized_html = { + sanitize_before_validation: true, + }; + } + + return validators; + } +} diff --git a/src/FileGeneration/FieldGenerators/NumberFieldGenerator.ts b/src/FileGeneration/FieldGenerators/NumberFieldGenerator.ts new file mode 100644 index 0000000..e2330b3 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/NumberFieldGenerator.ts @@ -0,0 +1,51 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { + ItemTypeBuilderAddMethods, + MethodNameToConfig, +} from "@/types/ItemTypeBuilderFields"; + +/** + * Base class for number field generators (Integer, Float) + * Consolidates common number field logic + */ +export abstract class NumberFieldGenerator< + TMethodName extends ItemTypeBuilderAddMethods, +> extends FieldGenerator { + generateBuildConfig(): MethodNameToConfig { + const config = this.createBaseConfig() as MethodNameToConfig; + const body = this.buildFieldBody(); + + if (this.hasBodyContent(body)) { + this.addOptionalProperty(config, "body", body); + } + + return config; + } + + protected override buildFieldBody(): NonNullable< + MethodNameToConfig["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addValidatorsToBody(body); + + return body; + } + + protected override addFieldSpecificValidators( + validators: Record, + ): void { + // Handle number-specific validators + if (this.field.validators?.["number_range"]) { + this.addOptionalProperty( + validators, + "number_range", + this.field.validators["number_range"], + ); + } + } +} diff --git a/src/FileGeneration/FieldGenerators/RichTextFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/RichTextFieldGenerator.test.ts new file mode 100644 index 0000000..dc259ce --- /dev/null +++ b/src/FileGeneration/FieldGenerators/RichTextFieldGenerator.test.ts @@ -0,0 +1,251 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { RichTextFieldGenerator } from "./RichTextFieldGenerator"; + +describe("RichTextFieldGenerator", () => { + let generator: RichTextFieldGenerator; + let itemTypeReferences: Map; + + beforeEach(() => { + itemTypeReferences = new Map([ + [ + "block-id-1", + { + id: "block-id-1", + name: "TestBlock", + modular_block: true, + } as ItemType, + ], + [ + "model-id-1", + { + id: "model-id-1", + name: "TestModel", + modular_block: false, + } as ItemType, + ], + ]); + }); + + describe("getMethodCallName", () => { + it("should return addModularContent", () => { + const field = createRichTextField(); + generator = new RichTextFieldGenerator({ field }); + expect(generator.getMethodCallName()).toBe("addModularContent"); + }); + }); + + describe("generateBuildConfig", () => { + it("should generate basic rich text field configuration", () => { + const field = createRichTextField({ + api_key: "test_rich_text", + label: "Test Rich Text", + hint: "Enter rich text content", + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.label).toBe("Test Rich Text"); + expect(config.body?.api_key).toBe("test_rich_text"); + expect(config.body?.hint).toBe("Enter rich text content"); + }); + + it("should handle start_collapsed parameter", () => { + const field = createRichTextField({ + appearance: { + editor: "rich_text", + addons: [], + parameters: { + start_collapsed: true, + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.start_collapsed).toBe(true); + }); + + it("should handle start_collapsed false", () => { + const field = createRichTextField({ + appearance: { + editor: "rich_text", + addons: [], + parameters: { + start_collapsed: false, + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.start_collapsed).toBe(false); + }); + + it("should process rich_text_blocks validator with getBlock calls", () => { + const field = createRichTextField({ + validators: { + rich_text_blocks: { + item_types: ["block-id-1"], + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.rich_text_blocks?.item_types).toEqual([ + { __async_call: 'await getBlock("TestBlock")' }, + ]); + }); + + it("should process size validator", () => { + const field = createRichTextField({ + validators: { + size: { + min: 1, + max: 5, + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.size).toEqual({ + min: 1, + max: 5, + }); + }); + + it("should handle multiple blocks in rich_text_blocks validator", () => { + itemTypeReferences.set("block-id-2", { + id: "block-id-2", + name: "AnotherBlock", + modular_block: true, + } as ItemType); + + const field = createRichTextField({ + validators: { + rich_text_blocks: { + item_types: ["block-id-1", "block-id-2"], + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.rich_text_blocks?.item_types).toEqual([ + { __async_call: 'await getBlock("TestBlock")' }, + { __async_call: 'await getBlock("AnotherBlock")' }, + ]); + }); + + it("should handle size validator", () => { + const field = createRichTextField({ + validators: { + size: { + min: 1, + max: 5, + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.size).toEqual({ + min: 1, + max: 5, + }); + }); + + it("should combine multiple validators", () => { + const field = createRichTextField({ + validators: { + rich_text_blocks: { + item_types: ["block-id-1"], + }, + size: { + min: 2, + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.rich_text_blocks?.item_types).toEqual([ + { __async_call: 'await getBlock("TestBlock")' }, + ]); + expect(config.body?.validators?.size).toEqual({ min: 2 }); + }); + + it("should handle default value", () => { + const field = createRichTextField({ + default_value: "Default content", + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.default_value).toBe("Default content"); + }); + }); + + describe("generateMethodCall", () => { + it("should generate proper method call string", () => { + const field = createRichTextField({ + api_key: "test_rich_text", + label: "Test Rich Text", + validators: { + rich_text_blocks: { + item_types: ["block-id-1"], + }, + }, + appearance: { + editor: "rich_text", + addons: [], + parameters: { + start_collapsed: false, + }, + }, + }); + + generator = new RichTextFieldGenerator({ field, itemTypeReferences }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain(".addModularContent({"); + expect(methodCall).toContain('label: "Test Rich Text"'); + expect(methodCall).toContain("start_collapsed: false"); + expect(methodCall).toContain('api_key: "test_rich_text"'); + expect(methodCall).toContain('await getBlock("TestBlock")'); + }); + }); + + function createRichTextField(overrides: Partial = {}): Field { + return { + id: "field-id", + api_key: "rich_text_field", + label: "Rich Text Field", + field_type: "rich_text", + localized: false, + validators: {}, + appearance: { + editor: "rich_text", + addons: [], + parameters: {}, + }, + default_value: null, + hint: null, + ...overrides, + } as Field; + } +}); diff --git a/src/FileGeneration/FieldGenerators/RichTextFieldGenerator.ts b/src/FileGeneration/FieldGenerators/RichTextFieldGenerator.ts new file mode 100644 index 0000000..4a1ea33 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/RichTextFieldGenerator.ts @@ -0,0 +1,64 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +/** + * Generates ItemTypeBuilder method calls for RichText field types. + * Handles rich_text fields which are used for ModularContent. + */ +export class RichTextFieldGenerator extends FieldGenerator<"addModularContent"> { + getMethodCallName() { + return "addModularContent" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addModularContent"> { + const config = + this.createBaseConfig() as MethodNameToConfig<"addModularContent">; + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addModularContent">["body"] + >; + + // Extract appearance parameters + const appearance = this.field.appearance; + const parameters = appearance?.parameters || {}; + + // Add start_collapsed configuration + if (typeof parameters.start_collapsed === "boolean") { + config.start_collapsed = parameters.start_collapsed; + } + + // Add validators (required for rich_text fields) + const validators = {} as Record; + + // Process rich_text_blocks validator (required) + if (this.field.validators?.rich_text_blocks) { + const richTextBlocks = { + ...this.field.validators.rich_text_blocks, + } as Record; + + // Convert item_types from IDs to getModel/getBlock calls + if (Array.isArray(richTextBlocks.item_types)) { + const getCalls = this.convertItemTypeIdsToGetCalls( + richTextBlocks.item_types as string[], + ); + richTextBlocks.item_types = getCalls; + } + + this.addOptionalProperty(validators, "rich_text_blocks", richTextBlocks); + } + + // Process size validator + if (this.field.validators?.size) { + this.addOptionalProperty(validators, "size", this.field.validators.size); + } + + if (Object.keys(validators).length > 0) { + this.addOptionalProperty(body, "validators", validators); + } + + // Add hint and default value + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + return { ...config, body }; + } +} diff --git a/src/FileGeneration/FieldGenerators/SeoFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/SeoFieldGenerator.test.ts new file mode 100644 index 0000000..dfb1789 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SeoFieldGenerator.test.ts @@ -0,0 +1,571 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { SeoConfig } from "@/Fields/Seo"; +import { SeoFieldGenerator } from "@/FileGeneration/FieldGenerators/SeoFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "seo", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "seo", + parameters: { + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("SeoFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate an SEO field with label", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "Page SEO", + api_key: "page_seo", + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "Page SEO", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "page_seo", + }, + } satisfies SeoConfig); + }); + + it("does not include position", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO without Position", + api_key: "seo_without_position", + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO without Position", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_without_position", + }, + } satisfies SeoConfig); + }); + + it("can generate an SEO field with a hint", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Hint", + api_key: "seo_with_hint", + hint: "Configure page SEO settings", + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO with Hint", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_with_hint", + hint: "Configure page SEO settings", + }, + } satisfies SeoConfig); + }); + + it("can generate an SEO field without a hint", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO without Hint", + api_key: "seo_without_hint", + hint: null, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO without Hint", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_without_hint", + }, + } satisfies SeoConfig); + }); + + it("should not include validators if none are set", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO without Validators", + api_key: "seo_without_validators", + validators: {}, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO without Validators", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_without_validators", + }, + } satisfies SeoConfig); + }); + }); + + describe("Appearance parameters", () => { + it("can extract custom fields parameter", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Custom Fields", + api_key: "seo_custom_fields", + appearance: { + addons: [], + editor: "seo", + parameters: { + fields: ["title", "description"], + previews: ["google", "twitter"], + }, + }, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO with Custom Fields", + fields: ["title", "description"], + previews: ["google", "twitter"], + body: { + api_key: "seo_custom_fields", + }, + } satisfies SeoConfig); + }); + + it("can extract custom previews parameter", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Custom Previews", + api_key: "seo_custom_previews", + appearance: { + addons: [], + editor: "seo", + parameters: { + fields: ["title", "description", "image"], + previews: ["google", "facebook"], + }, + }, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO with Custom Previews", + fields: ["title", "description", "image"], + previews: ["google", "facebook"], + body: { + api_key: "seo_custom_previews", + }, + } satisfies SeoConfig); + }); + + it("handles missing appearance parameters gracefully", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO No Params", + api_key: "seo_no_params", + appearance: { + addons: [], + editor: "seo", + parameters: {}, + }, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO No Params", + body: { + api_key: "seo_no_params", + }, + } satisfies SeoConfig); + }); + }); + + describe("With validators", () => { + describe("SEO-specific validators", () => { + it("can generate an SEO field with required_seo_fields validator", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Required Fields", + api_key: "seo_required_fields", + validators: { + required_seo_fields: { + title: true, + description: true, + }, + }, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO with Required Fields", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_required_fields", + validators: { + required_seo_fields: { + title: true, + description: true, + }, + }, + }, + } satisfies SeoConfig); + }); + + it("can generate an SEO field with title_length validator", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Title Length", + api_key: "seo_title_length", + validators: { + title_length: { min: 10, max: 60 }, + }, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO with Title Length", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_title_length", + validators: { + title_length: { min: 10, max: 60 }, + }, + }, + } satisfies SeoConfig); + }); + + it("can generate an SEO field with description_length validator", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Description Length", + api_key: "seo_description_length", + validators: { + description_length: { min: 120, max: 160 }, + }, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO with Description Length", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_description_length", + validators: { + description_length: { min: 120, max: 160 }, + }, + }, + } satisfies SeoConfig); + }); + + it("can generate an SEO field with multiple validators", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Multiple Validators", + api_key: "seo_multiple_validators", + validators: { + required_seo_fields: { + title: true, + }, + title_length: { min: 10, max: 60 }, + description_length: { min: 120, max: 160 }, + }, + }), + }); + + expect(seoGenerator.generateBuildConfig()).toEqual({ + label: "SEO with Multiple Validators", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "seo_multiple_validators", + validators: { + required_seo_fields: { + title: true, + }, + title_length: { min: 10, max: 60 }, + description_length: { min: 120, max: 160 }, + }, + }, + } satisfies SeoConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "Test SEO", + api_key: "test_seo", + }), + }); + + const methodCall = seoGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addSeo\(/); + expect(methodCall).toContain('"Test SEO"'); + expect(methodCall).toContain('"test_seo"'); + }); + + it("generates method call with custom fields", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "Custom Fields SEO", + api_key: "custom_fields_seo", + appearance: { + addons: [], + editor: "seo", + parameters: { + fields: ["title", "description"], + previews: ["google", "twitter"], + }, + }, + }), + }); + + const methodCall = seoGenerator.generateMethodCall(); + + expect(methodCall).toContain('"title"'); + expect(methodCall).toContain('"description"'); + expect(methodCall).toMatch(/\.addSeo\(/); + }); + + it("generates method call with custom previews", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "Custom Previews SEO", + api_key: "custom_previews_seo", + appearance: { + addons: [], + editor: "seo", + parameters: { + fields: ["title", "description", "image"], + previews: ["google", "facebook"], + }, + }, + }), + }); + + const methodCall = seoGenerator.generateMethodCall(); + + expect(methodCall).toContain('"google"'); + expect(methodCall).toContain('"facebook"'); + expect(methodCall).toMatch(/\.addSeo\(/); + }); + + it("generates method call with validators", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "SEO with Validators", + api_key: "seo_with_validators", + validators: { + title_length: { min: 10, max: 60 }, + }, + }), + }); + + const methodCall = seoGenerator.generateMethodCall(); + + expect(methodCall).toContain("title_length"); + expect(methodCall).toMatch(/\.addSeo\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic SEO field from DatoCMS", () => { + const apiResponseField: Field = { + id: "seo-field-123", + type: "field", + label: "Page SEO", + field_type: "seo", + api_key: "page_seo", + hint: "SEO settings for this page", + localized: false, + validators: { + required_seo_fields: { + title: true, + description: true, + }, + title_length: { min: 10, max: 60 }, + }, + position: 1, + appearance: { + addons: [], + editor: "seo", + parameters: { + fields: [ + "title", + "description", + "image", + "no_index", + "twitter_card", + ], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const seoGenerator = new SeoFieldGenerator({ + field: apiResponseField, + }); + + const config = seoGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Page SEO", + fields: ["title", "description", "image", "no_index", "twitter_card"], + previews: [ + "google", + "twitter", + "slack", + "whatsapp", + "telegram", + "facebook", + "linkedin", + ], + body: { + api_key: "page_seo", + hint: "SEO settings for this page", + validators: { + required_seo_fields: { + title: true, + description: true, + }, + title_length: { min: 10, max: 60 }, + }, + }, + } satisfies SeoConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const seoGenerator = new SeoFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(seoGenerator.getMethodCallName()).toBe("addSeo"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/SeoFieldGenerator.ts b/src/FileGeneration/FieldGenerators/SeoFieldGenerator.ts new file mode 100644 index 0000000..c34dd1a --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SeoFieldGenerator.ts @@ -0,0 +1,106 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addSeo() method calls. + */ +export class SeoFieldGenerator extends FieldGenerator<"addSeo"> { + getMethodCallName() { + return "addSeo" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addSeo"> { + const config = this.createBaseConfig() as MethodNameToConfig<"addSeo">; + const body = this.buildSeoFieldBody(); + + // Extract appearance parameters for the top-level config + this.addAppearanceParameters(config); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildSeoFieldBody(): NonNullable< + MethodNameToConfig<"addSeo">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addSeo">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addSeoValidators(body); + + return body; + } + + private addAppearanceParameters(config: MethodNameToConfig<"addSeo">): void { + const appearance = this.field.appearance; + + if (appearance?.parameters) { + const { fields, previews } = appearance.parameters; + + if (Array.isArray(fields)) { + config.fields = fields; + } + + if (Array.isArray(previews)) { + config.previews = previews; + } + } + } + + private addSeoValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + // SEO fields support specific validators but not 'required' + this.processSeoValidators(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + private processSeoValidators( + validators: NonNullable< + NonNullable["body"]>["validators"] + >, + ): void { + const fieldValidators = this.field.validators; + if (!fieldValidators) return; + + // Handle SEO-specific validators + if (fieldValidators.required_seo_fields) { + (validators as any).required_seo_fields = + fieldValidators.required_seo_fields; + } + if (fieldValidators.file_size) { + (validators as any).file_size = fieldValidators.file_size; + } + if (fieldValidators.image_dimensions) { + (validators as any).image_dimensions = fieldValidators.image_dimensions; + } + if (fieldValidators.image_aspect_ratio) { + (validators as any).image_aspect_ratio = + fieldValidators.image_aspect_ratio; + } + if (fieldValidators.title_length) { + (validators as any).title_length = fieldValidators.title_length; + } + if (fieldValidators.description_length) { + (validators as any).description_length = + fieldValidators.description_length; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/SingleAssetFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/SingleAssetFieldGenerator.test.ts new file mode 100644 index 0000000..7c4b6f1 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SingleAssetFieldGenerator.test.ts @@ -0,0 +1,415 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { SingleAssetConfig } from "@/Fields/SingleAsset"; +import { SingleAssetFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleAssetFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "file", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "file", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("SingleAssetFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a single asset field with label", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Document", + api_key: "document", + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Document", + body: { + api_key: "document", + }, + } satisfies SingleAssetConfig); + }); + + it("does not include position", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset without Position", + api_key: "asset_without_position", + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset without Position", + body: { + api_key: "asset_without_position", + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a single asset field with a hint", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Hint", + api_key: "asset_with_hint", + hint: "Upload a file", + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset with Hint", + body: { + api_key: "asset_with_hint", + hint: "Upload a file", + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a single asset field without a hint", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset without Hint", + api_key: "asset_without_hint", + hint: null, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset without Hint", + body: { + api_key: "asset_without_hint", + }, + } satisfies SingleAssetConfig); + }); + + it("should not include validators if none are set", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset without Validators", + api_key: "asset_without_validators", + validators: {}, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset without Validators", + body: { + api_key: "asset_without_validators", + }, + } satisfies SingleAssetConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required single asset field", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Required Asset", + api_key: "required_asset", + validators: { required: {} }, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Required Asset", + body: { + api_key: "required_asset", + validators: { required: true }, + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a non-required single asset field", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Non-Required Asset", + api_key: "non_required_asset", + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Asset", + body: { + api_key: "non_required_asset", + }, + } satisfies SingleAssetConfig); + }); + }); + + describe("File-specific validators", () => { + it("can generate a single asset field with file_size validator", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with File Size", + api_key: "asset_with_file_size", + validators: { + file_size: { max_value: 5, max_unit: "MB" }, + }, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset with File Size", + body: { + api_key: "asset_with_file_size", + validators: { + file_size: { max_value: 5, max_unit: "MB" }, + }, + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a single asset field with image_dimensions validator", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Image Dimensions", + api_key: "asset_with_dimensions", + validators: { + image_dimensions: { + width_min_value: 100, + width_max_value: 1920, + height_min_value: 100, + height_max_value: 1080, + }, + }, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset with Image Dimensions", + body: { + api_key: "asset_with_dimensions", + validators: { + image_dimensions: { + width_min_value: 100, + width_max_value: 1920, + height_min_value: 100, + height_max_value: 1080, + }, + }, + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a single asset field with image_aspect_ratio validator", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Aspect Ratio", + api_key: "asset_with_aspect_ratio", + validators: { + image_aspect_ratio: { eq_ar_numerator: 16, eq_ar_denominator: 9 }, + }, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset with Aspect Ratio", + body: { + api_key: "asset_with_aspect_ratio", + validators: { + image_aspect_ratio: { eq_ar_numerator: 16, eq_ar_denominator: 9 }, + }, + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a single asset field with extension validator", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Extensions", + api_key: "asset_with_extensions", + validators: { + extension: { predefined_list: "image" }, + }, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset with Extensions", + body: { + api_key: "asset_with_extensions", + validators: { + extension: { predefined_list: "image" }, + }, + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a single asset field with required_alt_title validator", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Alt Title", + api_key: "asset_with_alt_title", + validators: { + required_alt_title: { title: true, alt: true }, + }, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset with Alt Title", + body: { + api_key: "asset_with_alt_title", + validators: { + required_alt_title: { title: true, alt: true }, + }, + }, + } satisfies SingleAssetConfig); + }); + + it("can generate a single asset field with multiple validators", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Multiple Validators", + api_key: "asset_multiple_validators", + validators: { + required: {}, + file_size: { max_value: 10, max_unit: "MB" }, + extension: { predefined_list: "image" }, + }, + }), + }); + + expect(singleAssetGenerator.generateBuildConfig()).toEqual({ + label: "Asset with Multiple Validators", + body: { + api_key: "asset_multiple_validators", + validators: { + required: true, + file_size: { max_value: 10, max_unit: "MB" }, + extension: { predefined_list: "image" }, + }, + }, + } satisfies SingleAssetConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Test Asset", + api_key: "test_asset", + }), + }); + + const methodCall = singleAssetGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addSingleAsset\(/); + expect(methodCall).toContain('"Test Asset"'); + expect(methodCall).toContain('"test_asset"'); + }); + + it("generates method call with validators", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Validators", + api_key: "asset_with_validators", + validators: { + file_size: { max_value: 5, max_unit: "MB" }, + }, + }), + }); + + const methodCall = singleAssetGenerator.generateMethodCall(); + + expect(methodCall).toContain("file_size"); + expect(methodCall).toMatch(/\.addSingleAsset\(/); + }); + + it("generates method call with hint", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Asset with Hint", + api_key: "asset_with_hint", + hint: "Upload document", + }), + }); + + const methodCall = singleAssetGenerator.generateMethodCall(); + + expect(methodCall).toContain('"Upload document"'); + expect(methodCall).toMatch(/\.addSingleAsset\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic Single Asset field from DatoCMS", () => { + const apiResponseField: Field = { + id: "asset-field-123", + type: "field", + label: "Profile Image", + field_type: "file", + api_key: "profile_image", + hint: "Upload profile picture", + localized: false, + validators: { + required: {}, + file_size: { max_value: 2, max_unit: "MB" }, + extension: { predefined_list: "image" }, + required_alt_title: { title: false, alt: true }, + }, + position: 1, + appearance: { + addons: [], + editor: "file", + parameters: {}, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: apiResponseField, + }); + + const config = singleAssetGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Profile Image", + body: { + api_key: "profile_image", + hint: "Upload profile picture", + validators: { + required: true, + file_size: { max_value: 2, max_unit: "MB" }, + extension: { predefined_list: "image" }, + required_alt_title: { title: false, alt: true }, + }, + }, + } satisfies SingleAssetConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const singleAssetGenerator = new SingleAssetFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(singleAssetGenerator.getMethodCallName()).toBe("addSingleAsset"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/SingleAssetFieldGenerator.ts b/src/FileGeneration/FieldGenerators/SingleAssetFieldGenerator.ts new file mode 100644 index 0000000..746b5d2 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SingleAssetFieldGenerator.ts @@ -0,0 +1,84 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addSingleAsset() method calls. + */ +export class SingleAssetFieldGenerator extends FieldGenerator<"addSingleAsset"> { + getMethodCallName() { + return "addSingleAsset" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addSingleAsset"> { + const config = + this.createBaseConfig() as MethodNameToConfig<"addSingleAsset">; + const body = this.buildSingleAssetFieldBody(); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildSingleAssetFieldBody(): NonNullable< + MethodNameToConfig<"addSingleAsset">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addSingleAsset">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addSingleAssetValidators(body); + + return body; + } + + private addSingleAssetValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + this.processRequiredValidator(validators); + this.processSingleAssetValidators(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + private processSingleAssetValidators( + validators: NonNullable< + NonNullable["body"]>["validators"] + >, + ): void { + const fieldValidators = this.field.validators; + if (!fieldValidators) return; + + // Handle single asset specific validators (similar to gallery) + if (fieldValidators.file_size) { + (validators as any).file_size = fieldValidators.file_size; + } + if (fieldValidators.image_dimensions) { + (validators as any).image_dimensions = fieldValidators.image_dimensions; + } + if (fieldValidators.image_aspect_ratio) { + (validators as any).image_aspect_ratio = + fieldValidators.image_aspect_ratio; + } + if (fieldValidators.extension) { + (validators as any).extension = fieldValidators.extension; + } + if (fieldValidators.required_alt_title) { + (validators as any).required_alt_title = + fieldValidators.required_alt_title; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/SingleBlockFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/SingleBlockFieldGenerator.test.ts new file mode 100644 index 0000000..61daa08 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SingleBlockFieldGenerator.test.ts @@ -0,0 +1,277 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { SingleBlockFieldGenerator } from "./SingleBlockFieldGenerator"; + +describe("SingleBlockFieldGenerator", () => { + let generator: SingleBlockFieldGenerator; + let itemTypeReferences: Map; + + beforeEach(() => { + itemTypeReferences = new Map([ + [ + "block-id-1", + { + id: "block-id-1", + name: "TestBlock", + modular_block: true, + } as ItemType, + ], + [ + "model-id-1", + { + id: "model-id-1", + name: "TestModel", + modular_block: false, + } as ItemType, + ], + ]); + }); + + describe("getMethodCallName", () => { + it("should return addSingleBlock", () => { + const field = createSingleBlockField(); + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + expect(generator.getMethodCallName()).toBe("addSingleBlock"); + }); + }); + + describe("generateBuildConfig", () => { + it("should generate basic single block field configuration", () => { + const field = createSingleBlockField({ + api_key: "test_single_block", + label: "Test Single Block", + hint: "Select a single block", + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.label).toBe("Test Single Block"); + expect(config.body?.api_key).toBe("test_single_block"); + expect(config.body?.hint).toBe("Select a single block"); + }); + + it("should default to framed_single_block type", () => { + const field = createSingleBlockField(); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.type).toBe("framed_single_block"); + }); + + it("should set frameless_single_block type when editor is frameless", () => { + const field = createSingleBlockField({ + appearance: { + editor: "frameless_single_block", + addons: [], + parameters: {}, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.type).toBe("frameless_single_block"); + }); + + it("should handle start_collapsed parameter for framed type", () => { + const field = createSingleBlockField({ + appearance: { + editor: "single_block", + addons: [], + parameters: { + start_collapsed: true, + }, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.type).toBe("framed_single_block"); + expect(config.start_collapsed).toBe(true); + }); + + it("should not include start_collapsed for frameless type", () => { + const field = createSingleBlockField({ + appearance: { + editor: "frameless_single_block", + addons: [], + parameters: { + start_collapsed: true, + }, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.type).toBe("frameless_single_block"); + expect(config.start_collapsed).toBeUndefined(); + }); + + it("should process single_block_blocks validator with getBlock calls", () => { + const field = createSingleBlockField({ + validators: { + single_block_blocks: { + item_types: ["block-id-1"], + }, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.single_block_blocks?.item_types).toEqual([ + { __async_call: 'await getBlock("TestBlock")' }, + ]); + }); + + it("should handle multiple blocks in single_block_blocks validator", () => { + itemTypeReferences.set("block-id-2", { + id: "block-id-2", + name: "AnotherBlock", + modular_block: true, + } as ItemType); + + const field = createSingleBlockField({ + validators: { + single_block_blocks: { + item_types: ["block-id-1", "block-id-2"], + }, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.single_block_blocks?.item_types).toEqual([ + { __async_call: 'await getBlock("TestBlock")' }, + { __async_call: 'await getBlock("AnotherBlock")' }, + ]); + }); + + it("should handle required validator", () => { + const field = createSingleBlockField({ + validators: { + required: true, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.required).toBe(true); + }); + + it("should combine validators", () => { + const field = createSingleBlockField({ + validators: { + single_block_blocks: { + item_types: ["block-id-1"], + }, + required: true, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.single_block_blocks?.item_types).toEqual([ + { __async_call: 'await getBlock("TestBlock")' }, + ]); + expect(config.body?.validators?.required).toBe(true); + }); + + it("should handle default value", () => { + const field = createSingleBlockField({ + default_value: "default_block_id", + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const config = generator.generateBuildConfig(); + + expect(config.body?.default_value).toBe("default_block_id"); + }); + }); + + describe("generateMethodCall", () => { + it("should generate proper method call string for framed type", () => { + const field = createSingleBlockField({ + api_key: "test_single_block", + label: "Test Single Block", + validators: { + single_block_blocks: { + item_types: ["block-id-1"], + }, + }, + appearance: { + editor: "single_block", + addons: [], + parameters: { + start_collapsed: false, + }, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain(".addSingleBlock({"); + expect(methodCall).toContain('label: "Test Single Block"'); + expect(methodCall).toContain('type: "framed_single_block"'); + expect(methodCall).toContain("start_collapsed: false"); + expect(methodCall).toContain('api_key: "test_single_block"'); + expect(methodCall).toContain('await getBlock("TestBlock")'); + }); + + it("should generate proper method call string for frameless type", () => { + const field = createSingleBlockField({ + api_key: "test_frameless", + label: "Test Frameless", + appearance: { + editor: "frameless_single_block", + addons: [], + parameters: {}, + }, + validators: { + single_block_blocks: { + item_types: ["block-id-1"], + }, + }, + }); + + generator = new SingleBlockFieldGenerator({ field, itemTypeReferences }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain(".addSingleBlock({"); + expect(methodCall).toContain('label: "Test Frameless"'); + expect(methodCall).toContain('type: "frameless_single_block"'); + expect(methodCall).not.toContain("start_collapsed"); + expect(methodCall).toContain('await getBlock("TestBlock")'); + }); + }); + + function createSingleBlockField(overrides: Partial = {}): Field { + return { + id: "field-id", + api_key: "single_block_field", + label: "Single Block Field", + field_type: "single_block", + localized: false, + validators: {}, + appearance: { + editor: "single_block", + addons: [], + parameters: {}, + }, + default_value: null, + hint: null, + ...overrides, + } as Field; + } +}); diff --git a/src/FileGeneration/FieldGenerators/SingleBlockFieldGenerator.ts b/src/FileGeneration/FieldGenerators/SingleBlockFieldGenerator.ts new file mode 100644 index 0000000..025e6e2 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SingleBlockFieldGenerator.ts @@ -0,0 +1,65 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +/** + * Generates ItemTypeBuilder method calls for SingleBlock field types. + * Handles single_block fields with framed or frameless display options. + */ +export class SingleBlockFieldGenerator extends FieldGenerator<"addSingleBlock"> { + getMethodCallName() { + return "addSingleBlock" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addSingleBlock"> { + const config = this.createBaseConfig() as any; + const body = this.createBaseBody() as any; + + // Extract appearance parameters + const appearance = this.field.appearance; + const editor = appearance?.editor; + const parameters = appearance?.parameters || {}; + + // Set type based on editor + if (editor === "frameless_single_block") { + config.type = "frameless_single_block"; + } else { + config.type = "framed_single_block"; + + // Add start_collapsed for framed type only + if (typeof parameters.start_collapsed === "boolean") { + config.start_collapsed = parameters.start_collapsed; + } + } + + // Add validators (required for single_block fields) + const validators = {} as any; + + // Process single_block_blocks validator (required) + if (this.field.validators?.single_block_blocks) { + const singleBlockBlocks = { + ...this.field.validators.single_block_blocks, + } as any; + + // Convert item_types from IDs to getModel/getBlock calls + if (singleBlockBlocks.item_types) { + const getCalls = this.convertItemTypeIdsToGetCalls( + singleBlockBlocks.item_types, + ); + singleBlockBlocks.item_types = getCalls; + } + + validators.single_block_blocks = singleBlockBlocks; + } + + // Process required validator + this.processRequiredValidator(validators); + + body.validators = validators; + + // Add hint and default value + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + return { ...config, body }; + } +} diff --git a/src/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator.test.ts new file mode 100644 index 0000000..660c628 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator.test.ts @@ -0,0 +1,237 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { SingleLineStringFieldGenerator } from "@/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "string", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "test-item-type", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("SingleLineStringFieldGenerator", () => { + let singleLineStringGenerator: SingleLineStringFieldGenerator; + + beforeEach(() => { + singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "Test Single Line String", + api_key: "test_single_line_string", + }), + }); + }); + + describe("getMethodCallName", () => { + it("returns correct method call name", () => { + expect(singleLineStringGenerator.getMethodCallName()).toBe( + "addSingleLineString", + ); + }); + }); + + describe("generateBuildConfig", () => { + it("generates basic config without body or options when no additional properties", () => { + const config = singleLineStringGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Test Single Line String", + body: { + api_key: "test_single_line_string", + }, + }); + }); + + it("includes hint when present", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "String with Hint", + api_key: "string_with_hint", + hint: "This is a field hint", + }), + }); + + const config = singleLineStringGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a field hint"); + }); + + it("includes default value when present", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "String with Default", + api_key: "string_with_default", + default_value: "Default text", + }), + }); + + const config = singleLineStringGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("Default text"); + }); + + it("includes heading option when present", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "Heading String", + api_key: "heading_string", + appearance: { + addons: [], + editor: "single_line", + parameters: { + heading: true, + }, + } as any, + }), + }); + + const config = singleLineStringGenerator.generateBuildConfig(); + expect(config.options?.heading).toBe(true); + }); + + it("includes placeholder option when present", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "String with Placeholder", + api_key: "string_with_placeholder", + appearance: { + addons: [], + editor: "single_line", + parameters: { + placeholder: "Enter text here...", + }, + } as any, + }), + }); + + const config = singleLineStringGenerator.generateBuildConfig(); + expect(config.options?.placeholder).toBe("Enter text here..."); + }); + + it("includes both heading and placeholder options", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "Complex String", + api_key: "complex_string", + appearance: { + addons: [], + editor: "single_line", + parameters: { + heading: true, + placeholder: "Enter title here...", + }, + } as any, + }), + }); + + const config = singleLineStringGenerator.generateBuildConfig(); + expect(config.options?.heading).toBe(true); + expect(config.options?.placeholder).toBe("Enter title here..."); + }); + + it("excludes options when parameters are empty", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "String with Empty Parameters", + api_key: "string_with_empty_parameters", + appearance: { + addons: [], + editor: "single_line", + parameters: {}, + } as any, + }), + }); + + const config = singleLineStringGenerator.generateBuildConfig(); + expect(config.options).toBeUndefined(); + }); + + it("includes validators when present", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "Required String", + api_key: "required_string", + validators: { + required: true, + length: { + min: 5, + max: 100, + }, + } as any, + }), + }); + + const config = singleLineStringGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + expect(config.body?.validators?.length).toEqual({ + min: 5, + max: 100, + }); + }); + }); + + describe("generateMethodCall", () => { + it("generates basic method call", () => { + const methodCall = singleLineStringGenerator.generateMethodCall(); + expect(methodCall).toContain(".addSingleLineString("); + expect(methodCall).toContain('label: "Test Single Line String"'); + }); + + it("generates method call with options", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "Heading String", + api_key: "heading_string", + appearance: { + addons: [], + editor: "single_line", + parameters: { + heading: true, + placeholder: "Enter title...", + }, + } as any, + }), + }); + + const methodCall = singleLineStringGenerator.generateMethodCall(); + expect(methodCall).toContain(".addSingleLineString("); + expect(methodCall).toContain('label: "Heading String"'); + expect(methodCall).toContain("options:"); + expect(methodCall).toContain("heading: true"); + expect(methodCall).toContain('placeholder: "Enter title..."'); + }); + + it("generates method call with body properties", () => { + const singleLineStringGenerator = new SingleLineStringFieldGenerator({ + field: createMockField({ + label: "Complex String", + api_key: "complex_string", + hint: "Enter some text", + default_value: "Default value", + validators: { + required: true, + } as any, + }), + }); + + const methodCall = singleLineStringGenerator.generateMethodCall(); + expect(methodCall).toContain(".addSingleLineString("); + expect(methodCall).toContain('label: "Complex String"'); + expect(methodCall).toContain('api_key: "complex_string"'); + expect(methodCall).toContain('hint: "Enter some text"'); + expect(methodCall).toContain('default_value: "Default value"'); + expect(methodCall).toContain("required: true"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator.ts b/src/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator.ts new file mode 100644 index 0000000..f06799a --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SingleLineStringFieldGenerator.ts @@ -0,0 +1,31 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +export class SingleLineStringFieldGenerator extends FieldGenerator<"addSingleLineString"> { + getMethodCallName() { + return "addSingleLineString" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addSingleLineString"> { + const config = this.generateTemplateConfig(); + + // Extract appearance parameters using new template method + const options = this.extractAppearanceParameters< + NonNullable["options"]> + >({ + heading: { type: "boolean" }, + placeholder: { type: "string" }, + }); + + if (Object.keys(options).length > 0) { + config.options = options; + } + + return config; + } + + protected override addFieldSpecificValidators(validators: any): void { + // SingleLineString uses standard validators, so delegate to base class + this.processEnumValidator(validators); + } +} diff --git a/src/FileGeneration/FieldGenerators/SlugFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/SlugFieldGenerator.test.ts new file mode 100644 index 0000000..49107b4 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SlugFieldGenerator.test.ts @@ -0,0 +1,513 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { SlugConfig } from "@/Fields/Slug"; +import { SlugFieldGenerator } from "@/FileGeneration/FieldGenerators/SlugFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "slug", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "slug", + parameters: { + url_prefix: "", + placeholder: "", + }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("SlugFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a slug field with label", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "URL Slug", + api_key: "url_slug", + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "URL Slug", + url_prefix: "", + placeholder: "", + body: { + api_key: "url_slug", + }, + } satisfies SlugConfig); + }); + + it("does not include position", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug without Position", + api_key: "slug_without_position", + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug without Position", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_without_position", + }, + } satisfies SlugConfig); + }); + + it("can generate a slug field with a hint", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Hint", + api_key: "slug_with_hint", + hint: "URL-friendly slug", + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug with Hint", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_with_hint", + hint: "URL-friendly slug", + }, + } satisfies SlugConfig); + }); + + it("can generate a slug field without a hint", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug without Hint", + api_key: "slug_without_hint", + hint: null, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug without Hint", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_without_hint", + }, + } satisfies SlugConfig); + }); + + it("should not include validators if none are set", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug without Validators", + api_key: "slug_without_validators", + validators: {}, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug without Validators", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_without_validators", + }, + } satisfies SlugConfig); + }); + }); + + describe("Appearance parameters", () => { + it("can extract url_prefix parameter", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with URL Prefix", + api_key: "slug_with_prefix", + appearance: { + addons: [], + editor: "slug", + parameters: { + url_prefix: "https://example.com/", + placeholder: "", + }, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug with URL Prefix", + url_prefix: "https://example.com/", + placeholder: "", + body: { + api_key: "slug_with_prefix", + }, + } satisfies SlugConfig); + }); + + it("can extract placeholder parameter", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Placeholder", + api_key: "slug_with_placeholder", + appearance: { + addons: [], + editor: "slug", + parameters: { + url_prefix: "", + placeholder: "my-awesome-post", + }, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug with Placeholder", + url_prefix: "", + placeholder: "my-awesome-post", + body: { + api_key: "slug_with_placeholder", + }, + } satisfies SlugConfig); + }); + + it("can handle both url_prefix and placeholder", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Full Slug Config", + api_key: "full_slug_config", + appearance: { + addons: [], + editor: "slug", + parameters: { + url_prefix: "https://blog.example.com/posts/", + placeholder: "post-title-here", + }, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Full Slug Config", + url_prefix: "https://blog.example.com/posts/", + placeholder: "post-title-here", + body: { + api_key: "full_slug_config", + }, + } satisfies SlugConfig); + }); + + it("handles missing appearance parameters gracefully", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug No Params", + api_key: "slug_no_params", + appearance: { + addons: [], + editor: "slug", + parameters: {}, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug No Params", + body: { + api_key: "slug_no_params", + }, + } satisfies SlugConfig); + }); + }); + + describe("With validators", () => { + describe("Required", () => { + it("can generate a required slug field", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Required Slug", + api_key: "required_slug", + validators: { required: {} }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Required Slug", + url_prefix: "", + placeholder: "", + body: { + api_key: "required_slug", + validators: { required: true }, + }, + } satisfies SlugConfig); + }); + + it("can generate a non-required slug field", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Non-Required Slug", + api_key: "non_required_slug", + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Non-Required Slug", + url_prefix: "", + placeholder: "", + body: { + api_key: "non_required_slug", + }, + } satisfies SlugConfig); + }); + }); + + describe("Slug-specific validators", () => { + it("can generate a slug field with length validator", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Length", + api_key: "slug_with_length", + validators: { + length: { min: 5, max: 50 }, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug with Length", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_with_length", + validators: { + length: { min: 5, max: 50 }, + }, + }, + } satisfies SlugConfig); + }); + + it("can generate a slug field with slug_format validator", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Format", + api_key: "slug_with_format", + validators: { + slug_format: { predefined_pattern: "webpage_slug" }, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug with Format", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_with_format", + validators: { + slug_format: { predefined_pattern: "webpage_slug" }, + }, + }, + } satisfies SlugConfig); + }); + + it("can generate a slug field with slug_title_field validator", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Title Field", + api_key: "slug_with_title_field", + validators: { + slug_title_field: { title_field_id: "title_field_123" }, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug with Title Field", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_with_title_field", + validators: { + slug_title_field: { title_field_id: "title_field_123" }, + }, + }, + } satisfies SlugConfig); + }); + + it("can generate a slug field with multiple validators", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Multiple Validators", + api_key: "slug_multiple_validators", + validators: { + required: {}, + length: { min: 3, max: 100 }, + slug_format: { predefined_pattern: "webpage_slug" }, + }, + }), + }); + + expect(slugGenerator.generateBuildConfig()).toEqual({ + label: "Slug with Multiple Validators", + url_prefix: "", + placeholder: "", + body: { + api_key: "slug_multiple_validators", + validators: { + required: true, + length: { min: 3, max: 100 }, + slug_format: { predefined_pattern: "webpage_slug" }, + }, + }, + } satisfies SlugConfig); + }); + }); + }); + + describe("Method call generation", () => { + it("generates method call with correct method name", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Test Slug", + api_key: "test_slug", + }), + }); + + const methodCall = slugGenerator.generateMethodCall(); + + expect(methodCall).toMatch(/\.addSlug\(/); + expect(methodCall).toContain('"Test Slug"'); + expect(methodCall).toContain('"test_slug"'); + }); + + it("generates method call with appearance parameters", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Params", + api_key: "slug_with_params", + appearance: { + addons: [], + editor: "slug", + parameters: { + url_prefix: "https://example.com/", + placeholder: "post-slug", + }, + }, + }), + }); + + const methodCall = slugGenerator.generateMethodCall(); + + expect(methodCall).toContain('url_prefix: "https://example.com/"'); + expect(methodCall).toContain('placeholder: "post-slug"'); + expect(methodCall).toMatch(/\.addSlug\(/); + }); + + it("generates method call with validators", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Validators", + api_key: "slug_with_validators", + validators: { + length: { min: 5, max: 50 }, + }, + }), + }); + + const methodCall = slugGenerator.generateMethodCall(); + + expect(methodCall).toContain("length"); + expect(methodCall).toMatch(/\.addSlug\(/); + }); + + it("generates method call with hint", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Slug with Hint", + api_key: "slug_with_hint", + hint: "Enter URL slug", + }), + }); + + const methodCall = slugGenerator.generateMethodCall(); + + expect(methodCall).toContain('"Enter URL slug"'); + expect(methodCall).toMatch(/\.addSlug\(/); + }); + }); + + describe("Real-world API response test", () => { + it("can handle a realistic Slug field from DatoCMS", () => { + const apiResponseField: Field = { + id: "slug-field-123", + type: "field", + label: "Post Slug", + field_type: "slug", + api_key: "post_slug", + hint: "URL-friendly version of the title", + localized: false, + validators: { + required: {}, + length: { min: 3, max: 100 }, + slug_format: { predefined_pattern: "webpage_slug" }, + }, + position: 1, + appearance: { + addons: [], + editor: "slug", + parameters: { + url_prefix: "https://blog.example.com/posts/", + placeholder: "my-awesome-post", + }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + const slugGenerator = new SlugFieldGenerator({ + field: apiResponseField, + }); + + const config = slugGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Post Slug", + url_prefix: "https://blog.example.com/posts/", + placeholder: "my-awesome-post", + body: { + api_key: "post_slug", + hint: "URL-friendly version of the title", + validators: { + required: true, + length: { min: 3, max: 100 }, + slug_format: { predefined_pattern: "webpage_slug" }, + }, + }, + } satisfies SlugConfig); + }); + }); + + describe("getMethodCallName", () => { + it("returns the correct method name", () => { + const slugGenerator = new SlugFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(slugGenerator.getMethodCallName()).toBe("addSlug"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/SlugFieldGenerator.ts b/src/FileGeneration/FieldGenerators/SlugFieldGenerator.ts new file mode 100644 index 0000000..2d5a7e1 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/SlugFieldGenerator.ts @@ -0,0 +1,94 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addSlug() method calls. + */ +export class SlugFieldGenerator extends FieldGenerator<"addSlug"> { + getMethodCallName() { + return "addSlug" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addSlug"> { + const config = this.createBaseConfig() as MethodNameToConfig<"addSlug">; + const body = this.buildSlugFieldBody(); + + // Extract appearance parameters for the top-level config + this.addAppearanceParameters(config); + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildSlugFieldBody(): NonNullable< + MethodNameToConfig<"addSlug">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addSlug">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addSlugValidators(body); + + return body; + } + + private addAppearanceParameters(config: MethodNameToConfig<"addSlug">): void { + const appearance = this.field.appearance; + + if (appearance?.parameters) { + const { url_prefix, placeholder } = appearance.parameters; + + if (typeof url_prefix === "string") { + config.url_prefix = url_prefix; + } + + if (typeof placeholder === "string") { + config.placeholder = placeholder; + } + } + } + + private addSlugValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + this.processRequiredValidator(validators); + this.processSlugValidators(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + private processSlugValidators( + validators: NonNullable< + NonNullable["body"]>["validators"] + >, + ): void { + const fieldValidators = this.field.validators; + if (!fieldValidators) return; + + // Handle slug-specific validators + if (fieldValidators.length) { + (validators as any).length = fieldValidators.length; + } + if (fieldValidators.slug_format) { + (validators as any).slug_format = fieldValidators.slug_format; + } + if (fieldValidators.slug_title_field) { + (validators as any).slug_title_field = fieldValidators.slug_title_field; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator.test.ts new file mode 100644 index 0000000..dacbc94 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator.test.ts @@ -0,0 +1,230 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import { StringCheckboxGroupFieldGenerator } from "./StringCheckboxGroupFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "json", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "string_checkbox_group", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("StringCheckboxGroupFieldGenerator", () => { + describe("getMethodCallName", () => { + it("should return addStringCheckboxGroup", () => { + const field = createMockField({ + api_key: "checkbox_field", + label: "Checkbox Group", + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + expect(generator.getMethodCallName()).toBe("addStringCheckboxGroup"); + }); + }); + + describe("generateBuildConfig", () => { + it("should generate basic StringCheckboxGroup config with options", () => { + const field = createMockField({ + api_key: "checkbox_field", + label: "Checkbox Group", + appearance: { + editor: "string_checkbox_group", + parameters: { + options: [ + { label: "Feature A", value: "feature_a" }, + { label: "Feature B", value: "feature_b" }, + ], + }, + addons: [], + }, + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Checkbox Group", + options: [ + { label: "Feature A", value: "feature_a" }, + { label: "Feature B", value: "feature_b" }, + ], + }); + }); + + it("should handle options with hints", () => { + const field = createMockField({ + api_key: "checkbox_field", + label: "Checkbox Group", + appearance: { + editor: "string_checkbox_group", + parameters: { + options: [ + { + label: "Admin", + value: "admin", + hint: "Administrator privileges", + }, + { label: "Editor", value: "editor" }, + ], + }, + addons: [], + }, + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Checkbox Group", + options: [ + { label: "Admin", value: "admin", hint: "Administrator privileges" }, + { label: "Editor", value: "editor" }, + ], + }); + }); + + it("should include body with validators for required field", () => { + const field = createMockField({ + api_key: "required_checkbox", + label: "Required Checkbox Group", + appearance: { + editor: "string_checkbox_group", + parameters: { + options: [{ label: "Option 1", value: "option1" }], + }, + addons: [], + }, + validators: { + required: {}, + }, + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Required Checkbox Group", + options: [{ label: "Option 1", value: "option1" }], + body: { + api_key: "required_checkbox", + validators: { + required: true, + }, + }, + }); + }); + + it("should handle empty or missing options", () => { + const field = createMockField({ + api_key: "checkbox_field", + label: "Checkbox Group", + appearance: { + editor: "string_checkbox_group", + parameters: {}, + addons: [], + }, + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Checkbox Group", + options: [], + }); + }); + + it("should include hint and default value in body", () => { + const field = createMockField({ + api_key: "checkbox_field", + label: "Checkbox Group", + hint: "Select multiple features", + default_value: ["feature_a"] as any, + appearance: { + editor: "string_checkbox_group", + parameters: { + options: [{ label: "Feature A", value: "feature_a" }], + }, + addons: [], + }, + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Checkbox Group", + options: [{ label: "Feature A", value: "feature_a" }], + body: { + api_key: "checkbox_field", + hint: "Select multiple features", + default_value: ["feature_a"] as any, + }, + }); + }); + }); + + describe("generateMethodCall", () => { + it("should generate correct method call for StringCheckboxGroup", () => { + const field = createMockField({ + api_key: "checkbox_field", + label: "Checkbox Group", + appearance: { + editor: "string_checkbox_group", + parameters: { + options: [ + { label: "Feature A", value: "feature_a" }, + { label: "Feature B", value: "feature_b" }, + ], + }, + addons: [], + }, + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toBe( + `.addStringCheckboxGroup({ label: "Checkbox Group", options: [{ label: "Feature A", value: "feature_a" }, { label: "Feature B", value: "feature_b" }] })`, + ); + }); + + it("should generate correct method call with body for required field", () => { + const field = createMockField({ + api_key: "required_checkbox", + label: "Required Checkbox Group", + appearance: { + editor: "string_checkbox_group", + parameters: { + options: [{ label: "Feature A", value: "feature_a" }], + }, + addons: [], + }, + validators: { + required: {}, + }, + }); + + const generator = new StringCheckboxGroupFieldGenerator({ field }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toBe( + `.addStringCheckboxGroup({ label: "Required Checkbox Group", options: [{ label: "Feature A", value: "feature_a" }], body: { api_key: "required_checkbox", validators: { required: true } } })`, + ); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator.ts b/src/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator.ts new file mode 100644 index 0000000..357e7aa --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringCheckboxGroupFieldGenerator.ts @@ -0,0 +1,68 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +/** + * Generates ItemTypeBuilder method calls for StringCheckboxGroup field types. + * Handles JSON fields with string_checkbox_group editor. + */ +export class StringCheckboxGroupFieldGenerator extends FieldGenerator<"addStringCheckboxGroup"> { + getMethodCallName() { + return "addStringCheckboxGroup" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addStringCheckboxGroup"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody() as any; + + // Extract options from appearance parameters + const options = this.extractOptionsFromAppearance(); + + // Add validators if any exist + if (this.hasValidators()) { + const validators: NonNullable< + NonNullable< + MethodNameToConfig<"addStringCheckboxGroup">["body"] + >["validators"] + > = {}; + + this.processRequiredValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + // Add hint and default value + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + const result: MethodNameToConfig<"addStringCheckboxGroup"> = { + ...config, + options, + }; + + // Only include body if it has content beyond api_key + if (this.hasBodyContent(body) && Object.keys(body).length > 1) { + result.body = body; + } + + return result; + } + + private extractOptionsFromAppearance() { + const parameters = this.field.appearance?.parameters; + if ( + !parameters || + !parameters.options || + !Array.isArray(parameters.options) + ) { + return []; + } + + return parameters.options.map((option: any) => ({ + label: option.label || "", + value: option.value || "", + ...(option.hint && { hint: option.hint }), + })); + } +} diff --git a/src/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator.test.ts new file mode 100644 index 0000000..29f6f0c --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator.test.ts @@ -0,0 +1,226 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import { StringMultiSelectFieldGenerator } from "./StringMultiSelectFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "json", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { addons: [], editor: "string_multi_select", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("StringMultiSelectFieldGenerator", () => { + describe("getMethodCallName", () => { + it("should return addStringMultiSelect", () => { + const field = createMockField({ + api_key: "multi_select_field", + label: "Multi Select", + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + expect(generator.getMethodCallName()).toBe("addStringMultiSelect"); + }); + }); + + describe("generateBuildConfig", () => { + it("should generate basic StringMultiSelect config with options", () => { + const field = createMockField({ + api_key: "multi_select_field", + label: "Multi Select", + appearance: { + editor: "string_multi_select", + parameters: { + options: [ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2" }, + ], + }, + addons: [], + }, + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Multi Select", + options: [ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2" }, + ], + }); + }); + + it("should handle options with hints", () => { + const field = createMockField({ + api_key: "multi_select_field", + label: "Multi Select", + appearance: { + editor: "string_multi_select", + parameters: { + options: [ + { label: "Red", value: "red", hint: "The color red" }, + { label: "Blue", value: "blue" }, + ], + }, + addons: [], + }, + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Multi Select", + options: [ + { label: "Red", value: "red", hint: "The color red" }, + { label: "Blue", value: "blue" }, + ], + }); + }); + + it("should include body with validators for required field", () => { + const field = createMockField({ + api_key: "required_multi_select", + label: "Required Multi Select", + appearance: { + editor: "string_multi_select", + parameters: { + options: [{ label: "Option 1", value: "option1" }], + }, + addons: [], + }, + validators: { + required: {}, + }, + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Required Multi Select", + options: [{ label: "Option 1", value: "option1" }], + body: { + api_key: "required_multi_select", + validators: { + required: true, + }, + }, + }); + }); + + it("should handle empty or missing options", () => { + const field = createMockField({ + api_key: "multi_select_field", + label: "Multi Select", + appearance: { + editor: "string_multi_select", + parameters: {}, + addons: [], + }, + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Multi Select", + options: [], + }); + }); + + it("should include hint and default value in body", () => { + const field = createMockField({ + api_key: "multi_select_field", + label: "Multi Select", + hint: "Select multiple options", + default_value: ["option1"] as any, + appearance: { + editor: "string_multi_select", + parameters: { + options: [{ label: "Option 1", value: "option1" }], + }, + addons: [], + }, + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + const config = generator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Multi Select", + options: [{ label: "Option 1", value: "option1" }], + body: { + api_key: "multi_select_field", + hint: "Select multiple options", + default_value: ["option1"] as any, + }, + }); + }); + }); + + describe("generateMethodCall", () => { + it("should generate correct method call for StringMultiSelect", () => { + const field = createMockField({ + api_key: "multi_select_field", + label: "Multi Select", + appearance: { + editor: "string_multi_select", + parameters: { + options: [ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2" }, + ], + }, + addons: [], + }, + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toBe( + `.addStringMultiSelect({ label: "Multi Select", options: [{ label: "Option 1", value: "option1" }, { label: "Option 2", value: "option2" }] })`, + ); + }); + + it("should generate correct method call with body for required field", () => { + const field = createMockField({ + api_key: "required_multi_select", + label: "Required Multi Select", + appearance: { + editor: "string_multi_select", + parameters: { + options: [{ label: "Option 1", value: "option1" }], + }, + addons: [], + }, + validators: { + required: {}, + }, + }); + + const generator = new StringMultiSelectFieldGenerator({ field }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toBe( + `.addStringMultiSelect({ label: "Required Multi Select", options: [{ label: "Option 1", value: "option1" }], body: { api_key: "required_multi_select", validators: { required: true } } })`, + ); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator.ts b/src/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator.ts new file mode 100644 index 0000000..4edab9b --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringMultiSelectFieldGenerator.ts @@ -0,0 +1,68 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +/** + * Generates ItemTypeBuilder method calls for StringMultiSelect field types. + * Handles JSON fields with string_multi_select editor. + */ +export class StringMultiSelectFieldGenerator extends FieldGenerator<"addStringMultiSelect"> { + getMethodCallName() { + return "addStringMultiSelect" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addStringMultiSelect"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody() as any; + + // Extract options from appearance parameters + const options = this.extractOptionsFromAppearance(); + + // Add validators if any exist + if (this.hasValidators()) { + const validators: NonNullable< + NonNullable< + MethodNameToConfig<"addStringMultiSelect">["body"] + >["validators"] + > = {}; + + this.processRequiredValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } + + // Add hint and default value + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + const result: MethodNameToConfig<"addStringMultiSelect"> = { + ...config, + options, + }; + + // Only include body if it has content beyond api_key + if (this.hasBodyContent(body) && Object.keys(body).length > 1) { + result.body = body; + } + + return result; + } + + private extractOptionsFromAppearance() { + const parameters = this.field.appearance?.parameters; + if ( + !parameters || + !parameters.options || + !Array.isArray(parameters.options) + ) { + return []; + } + + return parameters.options.map((option: any) => ({ + label: option.label || "", + value: option.value || "", + ...(option.hint && { hint: option.hint }), + })); + } +} diff --git a/src/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator.test.ts new file mode 100644 index 0000000..3df62c3 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator.test.ts @@ -0,0 +1,212 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { StringRadioGroupFieldGenerator } from "@/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "string", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "test-item-type", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("StringRadioGroupFieldGenerator", () => { + let stringRadioGroupGenerator: StringRadioGroupFieldGenerator; + + beforeEach(() => { + stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Test String Radio Group", + api_key: "test_string_radio_group", + }), + }); + }); + + describe("getMethodCallName", () => { + it("returns correct method call name", () => { + expect(stringRadioGroupGenerator.getMethodCallName()).toBe( + "addStringRadioGroup", + ); + }); + }); + + describe("generateBuildConfig", () => { + it("generates basic config with empty radios when no parameters", () => { + const config = stringRadioGroupGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Test String Radio Group", + radios: [], + body: { + api_key: "test_string_radio_group", + }, + }); + }); + + it("includes hint when present", () => { + const stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Radio Group with Hint", + api_key: "radio_group_with_hint", + hint: "This is a field hint", + }), + }); + + const config = stringRadioGroupGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a field hint"); + }); + + it("extracts radios from appearance parameters", () => { + const stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Radio Group with Options", + api_key: "radio_group_with_options", + appearance: { + addons: [], + editor: "string_radio_group", + parameters: { + radios: [ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2", hint: "Second option" }, + ], + }, + } as any, + }), + }); + + const config = stringRadioGroupGenerator.generateBuildConfig(); + expect(config.radios).toEqual([ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2", hint: "Second option" }, + ]); + }); + + it("handles radios with all properties", () => { + const stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Full Radio Group", + api_key: "full_radio_group", + appearance: { + addons: [], + editor: "string_radio_group", + parameters: { + radios: [ + { label: "Yes", value: "yes", hint: "Positive choice" }, + { label: "No", value: "no", hint: "Negative choice" }, + { label: "Maybe", value: "maybe" }, + ], + }, + } as any, + }), + }); + + const config = stringRadioGroupGenerator.generateBuildConfig(); + expect(config.radios).toEqual([ + { label: "Yes", value: "yes", hint: "Positive choice" }, + { label: "No", value: "no", hint: "Negative choice" }, + { label: "Maybe", value: "maybe" }, + ]); + }); + + it("includes default value when present", () => { + const stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Radio Group with Default", + api_key: "radio_group_with_default", + default_value: "option1", + }), + }); + + const config = stringRadioGroupGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("option1"); + }); + + it("includes validators when present", () => { + const stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Required Radio Group", + api_key: "required_radio_group", + validators: { + required: true, + } as any, + }), + }); + + const config = stringRadioGroupGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + }); + }); + + describe("generateMethodCall", () => { + it("generates basic method call", () => { + const methodCall = stringRadioGroupGenerator.generateMethodCall(); + expect(methodCall).toContain(".addStringRadioGroup("); + expect(methodCall).toContain('label: "Test String Radio Group"'); + expect(methodCall).toContain("radios: []"); + }); + + it("generates method call with radios", () => { + const stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Radio Group with Options", + api_key: "radio_group_with_options", + appearance: { + addons: [], + editor: "string_radio_group", + parameters: { + radios: [ + { label: "Option A", value: "a" }, + { label: "Option B", value: "b", hint: "Second option" }, + ], + }, + } as any, + }), + }); + + const methodCall = stringRadioGroupGenerator.generateMethodCall(); + expect(methodCall).toContain(".addStringRadioGroup("); + expect(methodCall).toContain('label: "Radio Group with Options"'); + expect(methodCall).toContain("radios:"); + expect(methodCall).toContain('label: "Option A"'); + expect(methodCall).toContain('value: "a"'); + expect(methodCall).toContain('label: "Option B"'); + expect(methodCall).toContain('value: "b"'); + expect(methodCall).toContain('hint: "Second option"'); + }); + + it("generates method call with body properties", () => { + const stringRadioGroupGenerator = new StringRadioGroupFieldGenerator({ + field: createMockField({ + label: "Complex Radio Group", + api_key: "complex_radio_group", + hint: "Choose one option", + default_value: "option1", + validators: { + required: true, + } as any, + }), + }); + + const methodCall = stringRadioGroupGenerator.generateMethodCall(); + expect(methodCall).toContain(".addStringRadioGroup("); + expect(methodCall).toContain('label: "Complex Radio Group"'); + expect(methodCall).toContain('api_key: "complex_radio_group"'); + expect(methodCall).toContain('hint: "Choose one option"'); + expect(methodCall).toContain('default_value: "option1"'); + expect(methodCall).toContain("required: true"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator.ts b/src/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator.ts new file mode 100644 index 0000000..67c982c --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringRadioGroupFieldGenerator.ts @@ -0,0 +1,76 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +export class StringRadioGroupFieldGenerator extends FieldGenerator<"addStringRadioGroup"> { + getMethodCallName() { + return "addStringRadioGroup" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addStringRadioGroup"> { + const config = + this.createBaseConfig() as MethodNameToConfig<"addStringRadioGroup">; + const body = this.buildStringRadioGroupFieldBody(); + + const radios = this.extractRadios(); + + config.radios = radios; + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildStringRadioGroupFieldBody(): NonNullable< + MethodNameToConfig<"addStringRadioGroup">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addStringRadioGroup">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addStringRadioGroupValidators(body); + + return body; + } + + private extractRadios(): MethodNameToConfig<"addStringRadioGroup">["radios"] { + const parameters = this.extractAppearanceParameters<{ + radios: Array<{ label?: string; value?: string; hint?: string }>; + }>({ + radios: { type: "array", default: [] }, + }); + + const radios = parameters.radios || []; + + return radios.map((radio) => ({ + label: radio.label || "", + value: radio.value || "", + ...(radio.hint && { hint: radio.hint }), + })); + } + + private addStringRadioGroupValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable< + MethodNameToConfig<"addStringRadioGroup">["body"] + >["validators"] + >; + + // Process each validator type individually to ensure proper conversion + this.processRequiredValidator(validators); + this.processUniqueValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/StringSelectFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/StringSelectFieldGenerator.test.ts new file mode 100644 index 0000000..7e9bc33 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringSelectFieldGenerator.test.ts @@ -0,0 +1,210 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { StringSelectFieldGenerator } from "@/FileGeneration/FieldGenerators/StringSelectFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "string", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "test-item-type", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("StringSelectFieldGenerator", () => { + let stringSelectGenerator: StringSelectFieldGenerator; + + beforeEach(() => { + stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Test String Select", + api_key: "test_string_select", + }), + }); + }); + + describe("getMethodCallName", () => { + it("returns correct method call name", () => { + expect(stringSelectGenerator.getMethodCallName()).toBe("addStringSelect"); + }); + }); + + describe("generateBuildConfig", () => { + it("generates basic config with empty options when no parameters", () => { + const config = stringSelectGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Test String Select", + options: [], + body: { + api_key: "test_string_select", + }, + }); + }); + + it("includes hint when present", () => { + const stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Select with Hint", + api_key: "select_with_hint", + hint: "This is a field hint", + }), + }); + + const config = stringSelectGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a field hint"); + }); + + it("extracts options from appearance parameters", () => { + const stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Select with Options", + api_key: "select_with_options", + appearance: { + addons: [], + editor: "string_select", + parameters: { + options: [ + { label: "Choice 1", value: "choice1" }, + { label: "Choice 2", value: "choice2", hint: "Second choice" }, + ], + }, + } as any, + }), + }); + + const config = stringSelectGenerator.generateBuildConfig(); + expect(config.options).toEqual([ + { label: "Choice 1", value: "choice1" }, + { label: "Choice 2", value: "choice2", hint: "Second choice" }, + ]); + }); + + it("handles options with all properties", () => { + const stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Full Select", + api_key: "full_select", + appearance: { + addons: [], + editor: "string_select", + parameters: { + options: [ + { label: "Small", value: "sm", hint: "Small size" }, + { label: "Medium", value: "md", hint: "Medium size" }, + { label: "Large", value: "lg" }, + ], + }, + } as any, + }), + }); + + const config = stringSelectGenerator.generateBuildConfig(); + expect(config.options).toEqual([ + { label: "Small", value: "sm", hint: "Small size" }, + { label: "Medium", value: "md", hint: "Medium size" }, + { label: "Large", value: "lg" }, + ]); + }); + + it("includes default value when present", () => { + const stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Select with Default", + api_key: "select_with_default", + default_value: "choice1", + }), + }); + + const config = stringSelectGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("choice1"); + }); + + it("includes validators when present", () => { + const stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Required Select", + api_key: "required_select", + validators: { + required: true, + } as any, + }), + }); + + const config = stringSelectGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + }); + }); + + describe("generateMethodCall", () => { + it("generates basic method call", () => { + const methodCall = stringSelectGenerator.generateMethodCall(); + expect(methodCall).toContain(".addStringSelect("); + expect(methodCall).toContain('label: "Test String Select"'); + expect(methodCall).toContain("options: []"); + }); + + it("generates method call with options", () => { + const stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Select with Options", + api_key: "select_with_options", + appearance: { + addons: [], + editor: "string_select", + parameters: { + options: [ + { label: "Option A", value: "a" }, + { label: "Option B", value: "b", hint: "Second option" }, + ], + }, + } as any, + }), + }); + + const methodCall = stringSelectGenerator.generateMethodCall(); + expect(methodCall).toContain(".addStringSelect("); + expect(methodCall).toContain('label: "Select with Options"'); + expect(methodCall).toContain("options:"); + expect(methodCall).toContain('label: "Option A"'); + expect(methodCall).toContain('value: "a"'); + expect(methodCall).toContain('label: "Option B"'); + expect(methodCall).toContain('value: "b"'); + expect(methodCall).toContain('hint: "Second option"'); + }); + + it("generates method call with body properties", () => { + const stringSelectGenerator = new StringSelectFieldGenerator({ + field: createMockField({ + label: "Complex Select", + api_key: "complex_select", + hint: "Choose one option", + default_value: "option1", + validators: { + required: true, + } as any, + }), + }); + + const methodCall = stringSelectGenerator.generateMethodCall(); + expect(methodCall).toContain(".addStringSelect("); + expect(methodCall).toContain('label: "Complex Select"'); + expect(methodCall).toContain('api_key: "complex_select"'); + expect(methodCall).toContain('hint: "Choose one option"'); + expect(methodCall).toContain('default_value: "option1"'); + expect(methodCall).toContain("required: true"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/StringSelectFieldGenerator.ts b/src/FileGeneration/FieldGenerators/StringSelectFieldGenerator.ts new file mode 100644 index 0000000..5beac79 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StringSelectFieldGenerator.ts @@ -0,0 +1,69 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +export class StringSelectFieldGenerator extends FieldGenerator<"addStringSelect"> { + getMethodCallName() { + return "addStringSelect" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addStringSelect"> { + const config = + this.createBaseConfig() as MethodNameToConfig<"addStringSelect">; + const body = this.buildStringSelectFieldBody(); + + const options = this.extractOptions(); + + config.options = options; + + if (this.hasBodyContent(body)) { + config.body = body; + } + + return config; + } + + private buildStringSelectFieldBody(): NonNullable< + MethodNameToConfig<"addStringSelect">["body"] + > { + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addStringSelect">["body"] + >; + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + this.addStringSelectValidators(body); + + return body; + } + + private extractOptions(): MethodNameToConfig<"addStringSelect">["options"] { + const parameters = this.field.appearance?.parameters as any; + const options = parameters?.options || []; + + return options.map((option: any) => ({ + label: option.label || "", + value: option.value || "", + ...(option.hint && { hint: option.hint }), + })); + } + + private addStringSelectValidators( + body: NonNullable["body"]>, + ): void { + if (!this.hasValidators()) { + return; + } + + const validators = {} as NonNullable< + NonNullable["body"]>["validators"] + >; + + // Process each validator type individually to ensure proper conversion + this.processRequiredValidator(validators); + this.processUniqueValidator(validators); + + if (Object.keys(validators).length > 0) { + body.validators = validators; + } + } +} diff --git a/src/FileGeneration/FieldGenerators/StructuredTextFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/StructuredTextFieldGenerator.test.ts new file mode 100644 index 0000000..eba2721 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StructuredTextFieldGenerator.test.ts @@ -0,0 +1,396 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { StructuredTextFieldGenerator } from "./StructuredTextFieldGenerator"; + +describe("StructuredTextFieldGenerator", () => { + let generator: StructuredTextFieldGenerator; + let itemTypeReferences: Map; + + beforeEach(() => { + itemTypeReferences = new Map([ + [ + "block-id-1", + { + id: "block-id-1", + name: "TestBlock", + modular_block: true, + } as ItemType, + ], + [ + "model-id-1", + { + id: "model-id-1", + name: "Terminal", + modular_block: false, + } as ItemType, + ], + [ + "model-id-2", + { + id: "model-id-2", + name: "Airline", + modular_block: false, + } as ItemType, + ], + ]); + }); + + describe("getMethodCallName", () => { + it("should return addStructuredText", () => { + const field = createStructuredTextField(); + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + expect(generator.getMethodCallName()).toBe("addStructuredText"); + }); + }); + + describe("generateBuildConfig", () => { + it("should generate basic structured text field configuration", () => { + const field = createStructuredTextField({ + api_key: "test_structured_text", + label: "Test Structured Text", + hint: "Enter structured text content", + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.label).toBe("Test Structured Text"); + }); + + it("should handle appearance parameters", () => { + const field = createStructuredTextField({ + appearance: { + editor: "structured_text", + addons: [], + parameters: { + nodes: ["heading", "paragraph", "list"], + marks: ["strong", "emphasis"], + heading_levels: [1, 2, 3], + blocks_start_collapsed: true, + show_links_target_blank: false, + show_links_meta_editor: true, + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.nodes).toEqual(["heading", "paragraph", "list"]); + expect(config.marks).toEqual(["strong", "emphasis"]); + expect(config.heading_levels).toEqual([1, 2, 3]); + expect(config.blocks_start_collapsed).toBe(true); + expect(config.show_links_target_blank).toBe(false); + expect(config.show_links_meta_editor).toBe(true); + }); + + it("should handle structured_text_blocks validator with getBlock calls", () => { + const field = createStructuredTextField({ + validators: { + structured_text_blocks: { + item_types: ["block-id-1"], + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect( + config.body?.validators?.structured_text_blocks?.item_types, + ).toEqual([{ __async_call: 'await getBlock("TestBlock")' }]); + }); + + it("should handle structured_text_links validator with getModel calls", () => { + const field = createStructuredTextField({ + validators: { + structured_text_links: { + item_types: ["model-id-1", "model-id-2"], + on_publish_with_unpublished_references_strategy: "fail", + on_reference_unpublish_strategy: "delete_references", + on_reference_delete_strategy: "delete_references", + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect( + config.body?.validators?.structured_text_links?.item_types, + ).toEqual([ + { __async_call: 'await getModel("Terminal")' }, + { __async_call: 'await getModel("Airline")' }, + ]); + expect( + config.body?.validators?.structured_text_links + ?.on_publish_with_unpublished_references_strategy, + ).toBe("fail"); + expect( + config.body?.validators?.structured_text_links + ?.on_reference_unpublish_strategy, + ).toBe("delete_references"); + expect( + config.body?.validators?.structured_text_links + ?.on_reference_delete_strategy, + ).toBe("delete_references"); + }); + + it("should handle both blocks and links validators", () => { + const field = createStructuredTextField({ + validators: { + structured_text_blocks: { + item_types: ["block-id-1"], + }, + structured_text_links: { + item_types: ["model-id-1"], + on_publish_with_unpublished_references_strategy: "fail", + on_reference_unpublish_strategy: "delete_references", + on_reference_delete_strategy: "delete_references", + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect( + config.body?.validators?.structured_text_blocks?.item_types, + ).toEqual([{ __async_call: 'await getBlock("TestBlock")' }]); + expect( + config.body?.validators?.structured_text_links?.item_types, + ).toEqual([{ __async_call: 'await getModel("Terminal")' }]); + }); + + it("should handle length validator", () => { + const field = createStructuredTextField({ + validators: { + length: { + min: 10, + max: 500, + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.length).toEqual({ + min: 10, + max: 500, + }); + }); + + it("should handle structured_text_inline_blocks validator", () => { + const field = createStructuredTextField({ + validators: { + structured_text_inline_blocks: { + item_types: ["block-id-1"], + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.structured_text_inline_blocks).toEqual({ + item_types: ["block-id-1"], + }); + }); + + it("should combine multiple validators", () => { + const field = createStructuredTextField({ + validators: { + length: { min: 5 }, + structured_text_blocks: { + item_types: ["block-id-1"], + }, + structured_text_links: { + item_types: ["model-id-1"], + on_publish_with_unpublished_references_strategy: "fail", + on_reference_unpublish_strategy: "delete_references", + on_reference_delete_strategy: "delete_references", + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.body?.validators?.length).toEqual({ min: 5 }); + expect( + config.body?.validators?.structured_text_blocks?.item_types, + ).toEqual([{ __async_call: 'await getBlock("TestBlock")' }]); + expect( + config.body?.validators?.structured_text_links?.item_types, + ).toEqual([{ __async_call: 'await getModel("Terminal")' }]); + }); + + it("should handle default value", () => { + const field = createStructuredTextField({ + default_value: "Default structured content", + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.body?.default_value).toBe("Default structured content"); + }); + + it("should not include body if no content beyond api_key", () => { + const field = createStructuredTextField({ + api_key: "simple_field", + validators: {}, + hint: null, + default_value: null, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.body).toBeUndefined(); + }); + + it("should include body if hint is present", () => { + const field = createStructuredTextField({ + hint: "Enter some text", + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const config = generator.generateBuildConfig(); + + expect(config.body?.hint).toBe("Enter some text"); + }); + }); + + describe("generateMethodCall", () => { + it("should generate proper method call string with all features", () => { + const field = createStructuredTextField({ + api_key: "test_structured_text", + label: "Test Structured Text", + hint: "Enter rich content", + appearance: { + editor: "structured_text", + addons: [], + parameters: { + nodes: ["heading", "paragraph"], + marks: ["strong"], + heading_levels: [1, 2], + blocks_start_collapsed: false, + show_links_target_blank: true, + show_links_meta_editor: false, + }, + }, + validators: { + structured_text_blocks: { + item_types: ["block-id-1"], + }, + structured_text_links: { + item_types: ["model-id-1"], + on_publish_with_unpublished_references_strategy: "fail", + on_reference_unpublish_strategy: "delete_references", + on_reference_delete_strategy: "delete_references", + }, + }, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain(".addStructuredText({"); + expect(methodCall).toContain('label: "Test Structured Text"'); + expect(methodCall).toContain('nodes: ["heading", "paragraph"]'); + expect(methodCall).toContain('marks: ["strong"]'); + expect(methodCall).toContain("heading_levels: [1, 2]"); + expect(methodCall).toContain("blocks_start_collapsed: false"); + expect(methodCall).toContain("show_links_target_blank: true"); + expect(methodCall).toContain("show_links_meta_editor: false"); + expect(methodCall).toContain('api_key: "test_structured_text"'); + expect(methodCall).toContain('hint: "Enter rich content"'); + expect(methodCall).toContain('await getBlock("TestBlock")'); + expect(methodCall).toContain('await getModel("Terminal")'); + }); + + it("should generate minimal method call for simple field", () => { + const field = createStructuredTextField({ + api_key: "simple_field", + label: "Simple Field", + validators: {}, + hint: null, + default_value: null, + }); + + generator = new StructuredTextFieldGenerator({ + field, + itemTypeReferences, + }); + const methodCall = generator.generateMethodCall(); + + expect(methodCall).toContain(".addStructuredText({"); + expect(methodCall).toContain('label: "Simple Field"'); + expect(methodCall).not.toContain("body:"); + expect(methodCall).not.toContain("validators:"); + }); + }); + + function createStructuredTextField(overrides: Partial = {}): Field { + return { + id: "field-id", + api_key: "structured_text_field", + label: "Structured Text Field", + field_type: "structured_text", + localized: false, + validators: {}, + appearance: { + editor: "structured_text", + addons: [], + parameters: {}, + }, + default_value: null, + hint: null, + ...overrides, + } as Field; + } +}); diff --git a/src/FileGeneration/FieldGenerators/StructuredTextFieldGenerator.ts b/src/FileGeneration/FieldGenerators/StructuredTextFieldGenerator.ts new file mode 100644 index 0000000..7d16e37 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/StructuredTextFieldGenerator.ts @@ -0,0 +1,130 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +/** + * Generates ItemTypeBuilder method calls for StructuredText field types. + * Handles structured_text fields with rich text editing capabilities. + */ +export class StructuredTextFieldGenerator extends FieldGenerator<"addStructuredText"> { + getMethodCallName() { + return "addStructuredText" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addStructuredText"> { + const config = + this.createBaseConfig() as MethodNameToConfig<"addStructuredText">; + const body = this.createBaseBody() as NonNullable< + MethodNameToConfig<"addStructuredText">["body"] + >; + + // Extract appearance parameters + const appearance = this.field.appearance; + const parameters = appearance?.parameters || {}; + + // Add appearance-based configuration + if (parameters.nodes && Array.isArray(parameters.nodes)) { + config.nodes = parameters.nodes; + } + + if (parameters.marks && Array.isArray(parameters.marks)) { + config.marks = parameters.marks; + } + + if (parameters.heading_levels && Array.isArray(parameters.heading_levels)) { + config.heading_levels = parameters.heading_levels; + } + + if (typeof parameters.blocks_start_collapsed === "boolean") { + config.blocks_start_collapsed = parameters.blocks_start_collapsed; + } + + if (typeof parameters.show_links_target_blank === "boolean") { + config.show_links_target_blank = parameters.show_links_target_blank; + } + + if (typeof parameters.show_links_meta_editor === "boolean") { + config.show_links_meta_editor = parameters.show_links_meta_editor; + } + + // Add validators if any exist + if (this.hasValidators()) { + const validators: NonNullable< + NonNullable< + MethodNameToConfig<"addStructuredText">["body"] + >["validators"] + > = {}; + + // Process length validator + if (this.field.validators?.length) { + validators.length = this.field.validators.length; + } + + // Process structured_text_inline_blocks validator + if (this.field.validators?.structured_text_inline_blocks) { + // Inline blocks validator doesn't convert item_types - it passes them as-is + this.addOptionalProperty( + validators, + "structured_text_inline_blocks", + this.field.validators.structured_text_inline_blocks, + ); + } + + // Process structured_text_blocks validator + if (this.field.validators?.structured_text_blocks) { + const structuredTextBlocks = { + ...this.field.validators.structured_text_blocks, + } as Record; + + // Convert item_types from IDs to getModel/getBlock calls + if (Array.isArray(structuredTextBlocks.item_types)) { + const getCalls = this.convertItemTypeIdsToGetCalls( + structuredTextBlocks.item_types as string[], + ); + structuredTextBlocks.item_types = getCalls; + } + + this.addOptionalProperty( + validators, + "structured_text_blocks", + structuredTextBlocks, + ); + } + + // Process structured_text_links validator + if (this.field.validators?.structured_text_links) { + const structuredTextLinks = { + ...this.field.validators.structured_text_links, + } as Record; + + // Convert item_types from IDs to getModel/getBlock calls + if (Array.isArray(structuredTextLinks.item_types)) { + const getCalls = this.convertItemTypeIdsToGetCalls( + structuredTextLinks.item_types as string[], + ); + structuredTextLinks.item_types = getCalls; + } + + this.addOptionalProperty( + validators, + "structured_text_links", + structuredTextLinks, + ); + } + + if (Object.keys(validators).length > 0) { + this.addOptionalProperty(body, "validators", validators); + } + } + + // Add hint and default value + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + // Only include body if it has content beyond api_key + if (this.hasBodyContent(body) && Object.keys(body).length > 1) { + return { ...config, body }; + } + + return config; + } +} diff --git a/src/FileGeneration/FieldGenerators/TextareaFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/TextareaFieldGenerator.test.ts new file mode 100644 index 0000000..02b1fc8 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/TextareaFieldGenerator.test.ts @@ -0,0 +1,252 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { TextareaConfig } from "@/Fields/Textarea"; +import { TextareaFieldGenerator } from "@/FileGeneration/FieldGenerators/TextareaFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "text", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "textarea", + parameters: { placeholder: "Enter text here..." }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("TextareaFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a textarea field with label and placeholder", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Description", + api_key: "description", + }), + }); + + expect(textareaGenerator.generateBuildConfig()).toEqual({ + label: "Description", + placeholder: "Enter text here...", + body: { + api_key: "description", + }, + } satisfies TextareaConfig); + }); + + it("returns correct method call name", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(textareaGenerator.getMethodCallName()).toBe("addTextarea"); + }); + + it("includes hint when present", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Textarea with Hint", + api_key: "textarea_with_hint", + hint: "This is a hint", + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a hint"); + }); + + it("includes default_value when present", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Textarea with Default", + api_key: "textarea_with_default", + default_value: "Default text content", + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("Default text content"); + }); + + it("handles custom placeholder", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Custom Placeholder", + api_key: "custom_placeholder", + appearance: { + addons: [], + editor: "textarea", + parameters: { placeholder: "Custom placeholder text" }, + }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.placeholder).toBe("Custom placeholder text"); + }); + + it("handles missing placeholder parameter", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "No Placeholder", + api_key: "no_placeholder", + appearance: { + addons: [], + editor: "textarea", + parameters: {}, + }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.placeholder).toBeUndefined(); + }); + + it("handles empty placeholder", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Empty Placeholder", + api_key: "empty_placeholder", + appearance: { + addons: [], + editor: "textarea", + parameters: { placeholder: "" }, + }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.placeholder).toBeUndefined(); + }); + }); + + describe("Validators", () => { + it("includes required validator", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Required Textarea", + api_key: "required_textarea", + validators: { required: {} }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + }); + + it("includes length validator", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Length Textarea", + api_key: "length_textarea", + validators: { length: { min: 5, max: 200 } }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.body?.validators?.length).toEqual({ min: 5, max: 200 }); + }); + + it("includes format validator", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Format Textarea", + api_key: "format_textarea", + validators: { format: { predefined_pattern: "url" } }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.body?.validators?.format).toEqual({ + predefined_pattern: "url", + }); + }); + + it("includes sanitized_html validator", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Sanitized Textarea", + api_key: "sanitized_textarea", + validators: { sanitized_html: {} }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.body?.validators?.sanitized_html).toEqual({ + sanitize_before_validation: true, + }); + }); + + it("combines multiple validators", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Multi Validator Textarea", + api_key: "multi_validator_textarea", + validators: { + required: {}, + length: { min: 10, max: 300 }, + sanitized_html: {}, + }, + }), + }); + + const config = textareaGenerator.generateBuildConfig(); + expect(config.body?.validators).toEqual({ + required: true, + length: { min: 10, max: 300 }, + sanitized_html: { sanitize_before_validation: true }, + }); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call string", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Test Textarea", + api_key: "test_textarea", + hint: "Test hint", + }), + }); + + const methodCall = textareaGenerator.generateMethodCall(); + expect(methodCall).toContain(".addTextarea("); + expect(methodCall).toContain('label: "Test Textarea"'); + expect(methodCall).toContain('api_key: "test_textarea"'); + expect(methodCall).toContain('hint: "Test hint"'); + }); + + it("generates method call with placeholder", () => { + const textareaGenerator = new TextareaFieldGenerator({ + field: createMockField({ + label: "Test Textarea", + api_key: "test_textarea", + appearance: { + addons: [], + editor: "textarea", + parameters: { placeholder: "Type something..." }, + }, + }), + }); + + const methodCall = textareaGenerator.generateMethodCall(); + expect(methodCall).toContain('placeholder: "Type something..."'); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/TextareaFieldGenerator.ts b/src/FileGeneration/FieldGenerators/TextareaFieldGenerator.ts new file mode 100644 index 0000000..09b3624 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/TextareaFieldGenerator.ts @@ -0,0 +1,52 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +export class TextareaFieldGenerator extends FieldGenerator<"addTextarea"> { + getMethodCallName() { + return "addTextarea" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addTextarea"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody(); + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + const validators = this.createTextValidators(); + if (Object.keys(validators).length > 0) { + (body as any).validators = validators; + } + + const placeholder = this.field.appearance?.parameters?.placeholder as + | string + | undefined; + + return { + ...config, + ...(placeholder && { placeholder }), + ...(this.hasBodyContent(body) && { body }), + }; + } + + private createTextValidators() { + const validators: { + required?: boolean; + length?: { min?: number; max?: number }; + format?: { predefined_pattern?: string; custom_pattern?: RegExp }; + sanitized_html?: { sanitize_before_validation: boolean }; + } = {}; + + this.processRequiredValidator(validators); + this.processLengthValidator(validators); + this.processFormatValidator(validators); + + if (this.field.validators?.sanitized_html) { + validators.sanitized_html = { + sanitize_before_validation: true, + }; + } + + return validators; + } +} diff --git a/src/FileGeneration/FieldGenerators/UrlFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/UrlFieldGenerator.test.ts new file mode 100644 index 0000000..9a6af22 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/UrlFieldGenerator.test.ts @@ -0,0 +1,137 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { UrlFieldGenerator } from "@/FileGeneration/FieldGenerators/UrlFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "string", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: undefined as any, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "test-item-type", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("UrlFieldGenerator", () => { + let urlGenerator: UrlFieldGenerator; + + beforeEach(() => { + urlGenerator = new UrlFieldGenerator({ + field: createMockField({ + label: "Test URL", + api_key: "test_url", + }), + }); + }); + + describe("getMethodCallName", () => { + it("returns correct method call name", () => { + expect(urlGenerator.getMethodCallName()).toBe("addUrl"); + }); + }); + + describe("generateBuildConfig", () => { + it("generates basic config without body when no additional properties", () => { + const config = urlGenerator.generateBuildConfig(); + + expect(config).toEqual({ + label: "Test URL", + body: { + api_key: "test_url", + }, + }); + }); + + it("includes hint when present", () => { + const urlGenerator = new UrlFieldGenerator({ + field: createMockField({ + label: "URL with Hint", + api_key: "url_with_hint", + hint: "This is a field hint", + }), + }); + + const config = urlGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a field hint"); + }); + + it("includes default value when present", () => { + const urlGenerator = new UrlFieldGenerator({ + field: createMockField({ + label: "URL with Default", + api_key: "url_with_default", + default_value: "https://example.com", + }), + }); + + const config = urlGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("https://example.com"); + }); + + it("includes validators when present", () => { + const urlGenerator = new UrlFieldGenerator({ + field: createMockField({ + label: "Required URL", + api_key: "required_url", + validators: { + required: true, + format: { + predefined_pattern: "url", + }, + } as any, + }), + }); + + const config = urlGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + expect(config.body?.validators?.format).toEqual({ + predefined_pattern: "url", + }); + }); + }); + + describe("generateMethodCall", () => { + it("generates basic method call", () => { + const methodCall = urlGenerator.generateMethodCall(); + expect(methodCall).toContain(".addUrl("); + expect(methodCall).toContain('label: "Test URL"'); + }); + + it("generates method call with body properties", () => { + const urlGenerator = new UrlFieldGenerator({ + field: createMockField({ + label: "Complex URL", + api_key: "complex_url", + hint: "Enter a valid URL", + default_value: "https://example.com", + validators: { + required: true, + format: { + predefined_pattern: "url", + }, + } as any, + }), + }); + + const methodCall = urlGenerator.generateMethodCall(); + expect(methodCall).toContain(".addUrl("); + expect(methodCall).toContain('label: "Complex URL"'); + expect(methodCall).toContain('api_key: "complex_url"'); + expect(methodCall).toContain('hint: "Enter a valid URL"'); + expect(methodCall).toContain('default_value: "https://example.com"'); + expect(methodCall).toContain("required: true"); + expect(methodCall).toContain("format:"); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/UrlFieldGenerator.ts b/src/FileGeneration/FieldGenerators/UrlFieldGenerator.ts new file mode 100644 index 0000000..70874b1 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/UrlFieldGenerator.ts @@ -0,0 +1,16 @@ +import { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; + +/** + * Generates ItemTypeBuilder.addUrl() method calls. + * Uses base class template methods for standard behavior. + */ +export class UrlFieldGenerator extends FieldGenerator<"addUrl"> { + getMethodCallName() { + return "addUrl" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addUrl"> { + return this.generateTemplateConfig(); + } +} diff --git a/src/FileGeneration/FieldGenerators/WysiwygFieldGenerator.test.ts b/src/FileGeneration/FieldGenerators/WysiwygFieldGenerator.test.ts new file mode 100644 index 0000000..7849883 --- /dev/null +++ b/src/FileGeneration/FieldGenerators/WysiwygFieldGenerator.test.ts @@ -0,0 +1,281 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { describe, expect, it } from "@jest/globals"; +import type { WysiwygConfig } from "@/Fields/Wysiwyg"; +import { WysiwygFieldGenerator } from "@/FileGeneration/FieldGenerators/WysiwygFieldGenerator"; + +function createMockField( + overrides: Partial & Pick, +): Field { + const defaults: Omit = { + id: "test-id", + type: "field", + field_type: "text", + hint: null, + localized: false, + position: 1, + validators: {}, + appearance: { + addons: [], + editor: "wysiwyg", + parameters: { toolbar: ["format", "bold", "italic", "link"] }, + }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "SDGeMOa4Q3CRgEQTXg8jbg", type: "item_type" }, + fieldset: null, + }; + + return { ...defaults, ...overrides }; +} + +describe("WysiwygFieldGenerator", () => { + describe("Basic functionality", () => { + it("can generate a wysiwyg field with label and toolbar", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Rich Text Content", + api_key: "rich_text_content", + }), + }); + + expect(wysiwygGenerator.generateBuildConfig()).toEqual({ + label: "Rich Text Content", + toolbar: ["format", "bold", "italic", "link"], + body: { + api_key: "rich_text_content", + }, + } satisfies WysiwygConfig); + }); + + it("returns correct method call name", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Test", + api_key: "test", + }), + }); + + expect(wysiwygGenerator.getMethodCallName()).toBe("addWysiwyg"); + }); + + it("includes hint when present", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Wysiwyg with Hint", + api_key: "wysiwyg_with_hint", + hint: "This is a hint", + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.body?.hint).toBe("This is a hint"); + }); + + it("includes default_value when present", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Wysiwyg with Default", + api_key: "wysiwyg_with_default", + default_value: "

Default content

", + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.body?.default_value).toBe("

Default content

"); + }); + + it("handles comprehensive toolbar", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Full Toolbar Wysiwyg", + api_key: "full_toolbar_wysiwyg", + appearance: { + addons: [], + editor: "wysiwyg", + parameters: { + toolbar: [ + "format", + "bold", + "italic", + "strikethrough", + "code", + "ordered_list", + "unordered_list", + "quote", + "table", + "link", + "image", + "show_source", + "undo", + "redo", + "align_left", + "align_center", + "align_right", + "align_justify", + "outdent", + "indent", + "fullscreen", + ], + }, + }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.toolbar).toEqual([ + "format", + "bold", + "italic", + "strikethrough", + "code", + "ordered_list", + "unordered_list", + "quote", + "table", + "link", + "image", + "show_source", + "undo", + "redo", + "align_left", + "align_center", + "align_right", + "align_justify", + "outdent", + "indent", + "fullscreen", + ]); + }); + + it("handles empty toolbar", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Empty Toolbar Wysiwyg", + api_key: "empty_toolbar_wysiwyg", + appearance: { + addons: [], + editor: "wysiwyg", + parameters: { toolbar: [] }, + }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.toolbar).toEqual([]); + }); + + it("handles missing toolbar parameter", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "No Toolbar Wysiwyg", + api_key: "no_toolbar_wysiwyg", + appearance: { + addons: [], + editor: "wysiwyg", + parameters: {}, + }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.toolbar).toEqual([]); + }); + }); + + describe("Validators", () => { + it("includes required validator", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Required Wysiwyg", + api_key: "required_wysiwyg", + validators: { required: {} }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.body?.validators?.required).toBe(true); + }); + + it("includes length validator", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Length Wysiwyg", + api_key: "length_wysiwyg", + validators: { length: { min: 10, max: 500 } }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.body?.validators?.length).toEqual({ min: 10, max: 500 }); + }); + + it("includes format validator", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Format Wysiwyg", + api_key: "format_wysiwyg", + validators: { format: { custom_pattern: "^

.*

$" } }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.body?.validators?.format).toEqual({ + custom_pattern: "^

.*

$", + }); + }); + + it("includes sanitized_html validator", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Sanitized Wysiwyg", + api_key: "sanitized_wysiwyg", + validators: { sanitized_html: {} }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.body?.validators?.sanitized_html).toEqual({ + sanitize_before_validation: true, + }); + }); + + it("combines multiple validators", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Multi Validator Wysiwyg", + api_key: "multi_validator_wysiwyg", + validators: { + required: {}, + length: { min: 20, max: 1000 }, + sanitized_html: {}, + }, + }), + }); + + const config = wysiwygGenerator.generateBuildConfig(); + expect(config.body?.validators).toEqual({ + required: true, + length: { min: 20, max: 1000 }, + sanitized_html: { sanitize_before_validation: true }, + }); + }); + }); + + describe("Method call generation", () => { + it("generates correct method call string", () => { + const wysiwygGenerator = new WysiwygFieldGenerator({ + field: createMockField({ + label: "Test Wysiwyg", + api_key: "test_wysiwyg", + hint: "Test hint", + }), + }); + + const methodCall = wysiwygGenerator.generateMethodCall(); + expect(methodCall).toContain(".addWysiwyg("); + expect(methodCall).toContain('label: "Test Wysiwyg"'); + expect(methodCall).toContain('api_key: "test_wysiwyg"'); + expect(methodCall).toContain('hint: "Test hint"'); + }); + }); +}); diff --git a/src/FileGeneration/FieldGenerators/WysiwygFieldGenerator.ts b/src/FileGeneration/FieldGenerators/WysiwygFieldGenerator.ts new file mode 100644 index 0000000..382c07f --- /dev/null +++ b/src/FileGeneration/FieldGenerators/WysiwygFieldGenerator.ts @@ -0,0 +1,107 @@ +import type { MethodNameToConfig } from "@/types/ItemTypeBuilderFields"; +import { FieldGenerator } from "./FieldGenerator"; + +export class WysiwygFieldGenerator extends FieldGenerator<"addWysiwyg"> { + getMethodCallName() { + return "addWysiwyg" as const; + } + + generateBuildConfig(): MethodNameToConfig<"addWysiwyg"> { + const config = this.createBaseConfig(); + const body = this.createBaseBody(); + + this.addHintToBody(body); + this.addDefaultValueToBody(body); + + const validators = this.createTextValidators(); + if (Object.keys(validators).length > 0) { + (body as any).validators = validators; + } + + const toolbar = this.extractToolbarFromParameters() as Array< + | "format" + | "bold" + | "italic" + | "strikethrough" + | "code" + | "ordered_list" + | "unordered_list" + | "quote" + | "table" + | "link" + | "image" + | "show_source" + | "undo" + | "redo" + | "align_left" + | "align_center" + | "align_right" + | "align_justify" + | "outdent" + | "indent" + | "fullscreen" + >; + + return { + ...config, + toolbar, + ...(this.hasBodyContent(body) && { body }), + }; + } + + private createTextValidators() { + const validators: { + required?: boolean; + length?: { min?: number; max?: number }; + format?: { predefined_pattern?: string; custom_pattern?: string }; + sanitized_html?: { sanitize_before_validation: boolean }; + } = {}; + + this.processRequiredValidator(validators); + + if (this.field.validators?.length) { + const length = this.field.validators.length as any; + const lengthValidator: { min?: number; max?: number } = {}; + + if (length.min !== undefined) lengthValidator.min = length.min; + if (length.max !== undefined) lengthValidator.max = length.max; + + if (Object.keys(lengthValidator).length > 0) { + validators.length = lengthValidator; + } + } + + if (this.field.validators?.format) { + const format = this.field.validators.format as any; + const formatValidator: { + predefined_pattern?: string; + custom_pattern?: string; + } = {}; + + if (format.predefined_pattern) + formatValidator.predefined_pattern = format.predefined_pattern; + if (format.custom_pattern) + formatValidator.custom_pattern = format.custom_pattern; + + if (Object.keys(formatValidator).length > 0) { + validators.format = formatValidator; + } + } + + if (this.field.validators?.sanitized_html) { + validators.sanitized_html = { + sanitize_before_validation: true, + }; + } + + return validators; + } + + private extractToolbarFromParameters(): string[] { + const toolbar = this.field.appearance?.parameters?.toolbar; + if (Array.isArray(toolbar)) { + return toolbar; + } + return []; + } +} diff --git a/src/FileGeneration/FileGenerationService.test.ts b/src/FileGeneration/FileGenerationService.test.ts new file mode 100644 index 0000000..4b89411 --- /dev/null +++ b/src/FileGeneration/FileGenerationService.test.ts @@ -0,0 +1,399 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { FieldGeneratorFactory } from "@/FileGeneration/FieldGenerators/FieldGeneratorFactory"; +import { FileGenerationService } from "@/FileGeneration/FileGenerationService"; +import { FileGenerator } from "@/FileGeneration/FileGenerator"; + +// Mock dependencies +jest.mock("prettier", () => ({ + format: jest.fn(async (code: string) => code), +})); +jest.mock("@/FileGeneration/FileGenerator"); +jest.mock("@/FileGeneration/FieldGenerators/FieldGeneratorFactory"); + +const MockedFileGenerator = FileGenerator as jest.MockedClass< + typeof FileGenerator +>; +const MockedFieldGeneratorFactory = FieldGeneratorFactory as jest.MockedClass< + typeof FieldGeneratorFactory +>; + +describe("FileGenerationService", () => { + let service: FileGenerationService; + let mockItemType: ItemType; + let mockFields: Field[]; + let mockFileGenerator: jest.Mocked; + let mockFieldGeneratorFactory: jest.Mocked; + + beforeEach(() => { + jest.clearAllMocks(); + + service = new FileGenerationService(); + + mockItemType = { + id: "test-item-type-id", + type: "item_type", + name: "Test Item Type", + api_key: "test_item_type", + singleton: false, + sortable: true, + draft_mode_active: false, + all_locales_required: false, + hint: "Test hint", + collection_appearance: "table", + modular_block: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_field: null, + inverse_relationships_enabled: false, + title_field: null, + image_preview_field: null, + excerpt_field: null, + workflow: null, + has_singleton_item: false, + name_singular: null, + ordering_meta: null, + } as unknown as ItemType; + + mockFields = [ + { + id: "field-1", + type: "field", + label: "Test Field", + field_type: "string", + api_key: "test_field", + hint: "", + localized: false, + validators: {}, + position: 1, + appearance: { addons: [], editor: "single_line", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: mockItemType.id, type: "item_type" }, + fieldset: null, + }, + ]; + + mockFileGenerator = { + generate: jest.fn(), + } as any; + + mockFieldGeneratorFactory = {} as any; + + MockedFileGenerator.mockImplementation(() => mockFileGenerator); + MockedFieldGeneratorFactory.mockImplementation( + () => mockFieldGeneratorFactory, + ); + }); + + describe("setItemTypeReferences", () => { + it("should store item type references in a map", () => { + const itemTypes = [ + mockItemType, + { + ...mockItemType, + id: "second-item-type", + name: "Second Item Type", + api_key: "second_item_type", + }, + ]; + + service.setItemTypeReferences(itemTypes); + + // Test by generating a file and checking if references were passed + service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + expect.objectContaining({ + itemTypeReferences: expect.any(Map), + }), + expect.any(Object), + ); + + const passedConfig = MockedFileGenerator.mock.calls[0]?.[0]; + expect(passedConfig?.itemTypeReferences.size).toBe(2); + expect(passedConfig?.itemTypeReferences.get("test-item-type-id")).toEqual( + itemTypes[0], + ); + expect(passedConfig?.itemTypeReferences.get("second-item-type")).toEqual( + itemTypes[1], + ); + }); + + it("should clear existing references when setting new ones", () => { + // Set initial references + service.setItemTypeReferences([mockItemType]); + + // Set new references + const newItemType = { + ...mockItemType, + id: "new-item-type", + name: "New Item Type", + }; + service.setItemTypeReferences([newItemType]); + + service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }); + + const passedConfig = MockedFileGenerator.mock.calls[0]?.[0]; + expect(passedConfig?.itemTypeReferences.size).toBe(1); + expect(passedConfig?.itemTypeReferences.get("new-item-type")).toEqual( + newItemType, + ); + expect(passedConfig?.itemTypeReferences.has("test-item-type-id")).toBe( + false, + ); + }); + + it("should handle empty item types array", () => { + service.setItemTypeReferences([]); + + service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }); + + const passedConfig = MockedFileGenerator.mock.calls[0]?.[0]; + expect(passedConfig?.itemTypeReferences.size).toBe(0); + }); + }); + + describe("generateFile", () => { + beforeEach(() => { + service.setItemTypeReferences([mockItemType]); + }); + + it("should create FileGenerator with correct config and factory", async () => { + await service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + { + itemType: mockItemType, + fields: mockFields, + type: "block", + itemTypeReferences: expect.any(Map), + }, + mockFieldGeneratorFactory, + ); + }); + + it("should call generate on the FileGenerator instance", async () => { + const expectedResult = "generated file content"; + mockFileGenerator.generate.mockResolvedValue(expectedResult); + + const result = await service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }); + + expect(mockFileGenerator.generate).toHaveBeenCalledTimes(1); + expect(result).toBe(expectedResult); + }); + + it("should handle model type", async () => { + await service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "model", + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + expect.objectContaining({ + type: "model", + }), + mockFieldGeneratorFactory, + ); + }); + + it("should pass all fields to FileGenerator", async () => { + const multipleFields = [ + ...mockFields, + { + ...mockFields[0], + id: "field-2", + label: "Second Field", + api_key: "second_field", + position: 2, + type: "field" as const, + field_type: "string" as const, + localized: false, + default_value: null, + hint: "", + validators: {}, + appearance: { addons: [], editor: "single_line", parameters: {} }, + deep_filtering_enabled: false, + item_type: { id: mockItemType.id, type: "item_type" as const }, + fieldset: null, + }, + ]; + + await service.generateFile({ + itemType: mockItemType, + fields: multipleFields, + type: "block", + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + expect.objectContaining({ + fields: multipleFields, + }), + mockFieldGeneratorFactory, + ); + }); + + it("should reuse single FieldGeneratorFactory instance", async () => { + await service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }); + + await service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "model", + }); + + // Constructor only called once during service creation + expect(MockedFieldGeneratorFactory).toHaveBeenCalledTimes(1); + }); + + it("should propagate errors from FileGenerator", async () => { + const error = new Error("Generation failed"); + mockFileGenerator.generate.mockRejectedValue(error); + + await expect( + service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }), + ).rejects.toThrow("Generation failed"); + }); + + it("should pass localDevelopment parameter to FileGenerator", async () => { + await service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + localDevelopment: true, + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + expect.objectContaining({ + localDevelopment: true, + }), + expect.any(Object), + ); + }); + + it("should default localDevelopment to undefined when not provided", async () => { + await service.generateFile({ + itemType: mockItemType, + fields: mockFields, + type: "block", + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + expect.objectContaining({ + localDevelopment: undefined, + }), + expect.any(Object), + ); + }); + }); + + describe("integration scenarios", () => { + it("should handle complex field configurations", async () => { + const complexFields: Field[] = [ + { + id: "rich-text-field", + type: "field", + label: "Rich Text", + field_type: "rich_text", + api_key: "rich_text", + hint: "Rich text field with block references", + localized: true, + validators: { + rich_text_blocks: { + item_types: ["block-1", "block-2"], + }, + }, + position: 1, + appearance: { addons: [], editor: "wysiwyg", parameters: {} }, + default_value: null, + deep_filtering_enabled: true, + item_type: { id: mockItemType.id, type: "item_type" }, + fieldset: null, + }, + { + id: "date-field", + type: "field", + label: "Date Field", + field_type: "date", + api_key: "date_field", + hint: "", + localized: false, + validators: { + required: {}, + date_range: { + min: "2023-01-01", + max: "2025-12-31", + }, + }, + position: 2, + appearance: { addons: [], editor: "date_picker", parameters: {} }, + default_value: "2024-01-01", + deep_filtering_enabled: false, + item_type: { id: mockItemType.id, type: "item_type" }, + fieldset: null, + }, + ]; + + await service.generateFile({ + itemType: mockItemType, + fields: complexFields, + type: "block", + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + expect.objectContaining({ + fields: complexFields, + }), + mockFieldGeneratorFactory, + ); + }); + + it("should handle empty fields array", async () => { + await service.generateFile({ + itemType: mockItemType, + fields: [], + type: "block", + }); + + expect(MockedFileGenerator).toHaveBeenCalledWith( + expect.objectContaining({ + fields: [], + }), + mockFieldGeneratorFactory, + ); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerationService.ts b/src/FileGeneration/FileGenerationService.ts new file mode 100644 index 0000000..eb69b2e --- /dev/null +++ b/src/FileGeneration/FileGenerationService.ts @@ -0,0 +1,54 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { FieldGeneratorFactory } from "@/FileGeneration/FieldGenerators/FieldGeneratorFactory"; +import { FileGenerator } from "@/FileGeneration/FileGenerator"; + +export type FileGenerationRequest = { + itemType: ItemType; + fields: Field[]; + type: "block" | "model"; + localDevelopment?: boolean; +}; + +/** + * Service responsible for orchestrating file generation + * Simplified to remove unnecessary abstractions + */ +export class FileGenerationService { + private readonly itemTypeReferences: Map = new Map(); + private readonly fieldGeneratorFactory: FieldGeneratorFactory; + + constructor() { + this.fieldGeneratorFactory = new FieldGeneratorFactory(); + } + + /** + * Set references to all item types for resolving dependencies + */ + public setItemTypeReferences(itemTypes: ItemType[]): void { + this.itemTypeReferences.clear(); + for (const itemType of itemTypes) { + this.itemTypeReferences.set(itemType.id, itemType); + } + } + + /** + * Generate a TypeScript file for a block or model + */ + public async generateFile(request: FileGenerationRequest): Promise { + const fileGenerator = new FileGenerator( + { + itemType: request.itemType, + fields: request.fields, + type: request.type, + itemTypeReferences: this.itemTypeReferences, + localDevelopment: request.localDevelopment, + }, + this.fieldGeneratorFactory, + ); + + return fileGenerator.generate(); + } +} diff --git a/src/FileGeneration/FileGenerator.test.ts b/src/FileGeneration/FileGenerator.test.ts new file mode 100644 index 0000000..bca4015 --- /dev/null +++ b/src/FileGeneration/FileGenerator.test.ts @@ -0,0 +1,532 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import type { Options } from "prettier"; +import { FieldGeneratorFactory } from "@/FileGeneration/FieldGenerators/FieldGeneratorFactory"; +import { + FileGenerator, + type FileGeneratorConfig, +} from "@/FileGeneration/FileGenerator"; +import { + BlockReferenceAnalyzer, + type UsedFunctions, +} from "@/FileGeneration/FileGenerators/BlockReferenceAnalyzer"; +import { BuilderConfigGenerator } from "@/FileGeneration/FileGenerators/BuilderConfigGenerator"; +import { FieldMethodGenerator } from "@/FileGeneration/FileGenerators/FieldMethodGenerator"; +import { FunctionGenerator } from "@/FileGeneration/FileGenerators/FunctionGenerator"; +import { CodeFormatter } from "@/FileGeneration/FileGenerators/formatters/CodeFormatter"; +import { ImportGenerator } from "@/FileGeneration/FileGenerators/ImportGenerator"; + +// Mock all dependencies +jest.mock("prettier", () => ({ + format: jest.fn(async (code: string) => code), +})); +jest.mock("@/FileGeneration/FieldGenerators/FieldGeneratorFactory"); +jest.mock("@/FileGeneration/FileGenerators/BlockReferenceAnalyzer"); +jest.mock("@/FileGeneration/FileGenerators/BuilderConfigGenerator"); +jest.mock("@/FileGeneration/FileGenerators/FieldMethodGenerator"); +jest.mock("@/FileGeneration/FileGenerators/formatters/CodeFormatter"); +jest.mock("@/FileGeneration/FileGenerators/ImportGenerator"); +jest.mock("@/FileGeneration/FileGenerators/FunctionGenerator"); +jest.mock("@/utils/utils", () => ({ + toPascalCase: jest.fn((str: string) => `Pascal${str.replace(/\s+/g, "")}`), +})); + +const MockedFieldGeneratorFactory = FieldGeneratorFactory as jest.MockedClass< + typeof FieldGeneratorFactory +>; +const MockedBlockReferenceAnalyzer = BlockReferenceAnalyzer as jest.MockedClass< + typeof BlockReferenceAnalyzer +>; +const MockedBuildConfigGenerator = BuilderConfigGenerator as jest.MockedClass< + typeof BuilderConfigGenerator +>; +const MockedFieldMethodGenerator = FieldMethodGenerator as jest.MockedClass< + typeof FieldMethodGenerator +>; +const MockedCodeFormatter = CodeFormatter as jest.MockedClass< + typeof CodeFormatter +>; +const MockedImportGenerator = ImportGenerator as jest.MockedClass< + typeof ImportGenerator +>; +const MockedFunctionGenerator = FunctionGenerator as jest.MockedClass< + typeof FunctionGenerator +>; + +describe("FileGenerator", () => { + let mockConfig: FileGeneratorConfig; + let mockFieldGeneratorFactory: jest.Mocked; + let mockBlockReferenceAnalyzer: jest.Mocked; + let mockBuilderConfigGenerator: jest.Mocked; + let mockFieldMethodGenerator: jest.Mocked; + let mockCodeFormatter: jest.Mocked; + let mockImportGenerator: jest.Mocked; + let mockFunctionGenerator: jest.Mocked; + let mockItemType: ItemType; + let mockFields: Field[]; + let fileGenerator: FileGenerator; + let noAsyncFunctions: UsedFunctions; + let bothFunctions: UsedFunctions; + + beforeEach(() => { + jest.clearAllMocks(); + + // Initialize usedFunctions scenarios + noAsyncFunctions = { needsGetModel: false, needsGetBlock: false }; + bothFunctions = { needsGetModel: true, needsGetBlock: true }; + + // Set up default mock return values + mockBlockReferenceAnalyzer = { + hasBlockReferences: jest.fn().mockReturnValue(false), + getUsedFunctions: jest.fn().mockReturnValue(noAsyncFunctions), + } as any; + + mockItemType = { + id: "test-item-type-id", + type: "item_type", + name: "Test Block Name", + api_key: "test_block_name", + singleton: false, + sortable: true, + draft_mode_active: false, + all_locales_required: false, + hint: "Test hint", + collection_appearance: "table", + modular_block: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_field: null, + inverse_relationships_enabled: false, + title_field: null, + image_preview_field: null, + excerpt_field: null, + workflow: null, + has_singleton_item: false, + name_singular: null, + ordering_meta: null, + } as unknown as ItemType; + + mockFields = [ + { + id: "field-1", + type: "field", + label: "Test Field", + field_type: "string", + api_key: "test_field", + hint: "", + localized: false, + validators: {}, + position: 1, + appearance: { addons: [], editor: "single_line", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: mockItemType.id, type: "item_type" }, + fieldset: null, + }, + ]; + + mockConfig = { + itemType: mockItemType, + fields: mockFields, + type: "block", + itemTypeReferences: new Map(), + formatting: { printWidth: 100 } as Options, + }; + + // Set up mocks + mockFieldGeneratorFactory = {} as any; + mockBuilderConfigGenerator = { + generateBuilderConfig: jest.fn(), + } as any; + mockFieldMethodGenerator = { + generateFieldMethods: jest.fn(), + } as any; + mockCodeFormatter = { + format: jest.fn(), + } as any; + mockImportGenerator = { + generateImports: jest.fn(), + } as any; + mockFunctionGenerator = { + generateFunction: jest.fn(), + } as any; + + MockedFieldGeneratorFactory.mockImplementation( + () => mockFieldGeneratorFactory, + ); + MockedBlockReferenceAnalyzer.mockImplementation( + () => mockBlockReferenceAnalyzer, + ); + MockedBuildConfigGenerator.mockImplementation( + () => mockBuilderConfigGenerator, + ); + MockedFieldMethodGenerator.mockImplementation( + () => mockFieldMethodGenerator, + ); + MockedCodeFormatter.mockImplementation(() => mockCodeFormatter); + MockedImportGenerator.mockImplementation(() => mockImportGenerator); + MockedFunctionGenerator.mockImplementation(() => mockFunctionGenerator); + + fileGenerator = new FileGenerator(mockConfig, mockFieldGeneratorFactory); + }); + + describe("constructor", () => { + it("should initialize all generators with correct dependencies", () => { + expect(MockedBlockReferenceAnalyzer).toHaveBeenCalledTimes(1); + expect(MockedBuildConfigGenerator).toHaveBeenCalledTimes(1); + expect(MockedFieldMethodGenerator).toHaveBeenCalledTimes(1); + expect(MockedFieldMethodGenerator).toHaveBeenCalledWith( + mockFieldGeneratorFactory, + mockConfig.itemTypeReferences, + ); + expect(MockedImportGenerator).toHaveBeenCalledTimes(1); + expect(MockedFunctionGenerator).toHaveBeenCalledTimes(1); + expect(MockedFunctionGenerator).toHaveBeenCalledWith( + expect.any(Object), + expect.any(Object), + ); + expect(MockedCodeFormatter).toHaveBeenCalledWith(mockConfig.formatting); + }); + + it("should handle missing formatting config", () => { + const configWithoutFormatting = { ...mockConfig }; + delete configWithoutFormatting.formatting; + + new FileGenerator(configWithoutFormatting, mockFieldGeneratorFactory); + + expect(MockedCodeFormatter).toHaveBeenCalledWith(undefined); + }); + }); + + describe("generate", () => { + beforeEach(() => { + mockBlockReferenceAnalyzer.hasBlockReferences.mockReturnValue(false); + mockBlockReferenceAnalyzer.getUsedFunctions.mockReturnValue( + noAsyncFunctions, + ); + mockImportGenerator.generateImports.mockReturnValue("import statements"); + mockFunctionGenerator.generateFunction.mockReturnValue( + "function definition", + ); + mockCodeFormatter.format.mockResolvedValue("formatted code"); + }); + + it("should generate block file with correct function name", async () => { + const result = await fileGenerator.generate(); + + expect(mockBlockReferenceAnalyzer.getUsedFunctions).toHaveBeenCalledWith( + mockFields, + mockConfig.itemTypeReferences, + ); + expect(mockImportGenerator.generateImports).toHaveBeenCalledWith( + "BlockBuilder", + ); + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockName", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + expect(mockCodeFormatter.format).toHaveBeenCalledWith( + "import statements\n\nfunction definition", + ); + expect(result).toBe("formatted code"); + }); + + it("should generate model file with correct function name", async () => { + const modelConfig = { ...mockConfig, type: "model" as const }; + const modelGenerator = new FileGenerator( + modelConfig, + mockFieldGeneratorFactory, + ); + + await modelGenerator.generate(); + + expect(mockImportGenerator.generateImports).toHaveBeenCalledWith( + "ModelBuilder", + ); + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockName", + "ModelBuilder", + false, + noAsyncFunctions, + mockItemType, + "model", + mockFields, + ); + }); + + it("should detect when async is needed for block references", async () => { + mockBlockReferenceAnalyzer.getUsedFunctions.mockReturnValue( + bothFunctions, + ); + + await fileGenerator.generate(); + + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockName", + "BlockBuilder", + true, + bothFunctions, + mockItemType, + "block", + mockFields, + ); + }); + + it("should handle rich text fields that reference blocks", async () => { + const fieldsWithBlockRef = [ + ...mockFields, + { + id: "rich-text-field", + type: "field", + label: "Rich Text", + field_type: "rich_text", + api_key: "rich_text", + hint: "", + localized: false, + validators: { + rich_text_blocks: { + item_types: ["block-1", "block-2"], + }, + }, + position: 2, + appearance: { addons: [], editor: "wysiwyg", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: mockItemType.id, type: "item_type" }, + fieldset: null, + } as any, + ]; + + const configWithBlockRef = { ...mockConfig, fields: fieldsWithBlockRef }; + const generatorWithBlockRef = new FileGenerator( + configWithBlockRef, + mockFieldGeneratorFactory, + ); + + mockBlockReferenceAnalyzer.getUsedFunctions.mockReturnValue( + bothFunctions, + ); + + await generatorWithBlockRef.generate(); + + expect(mockBlockReferenceAnalyzer.getUsedFunctions).toHaveBeenCalledWith( + fieldsWithBlockRef, + configWithBlockRef.itemTypeReferences, + ); + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockName", + "BlockBuilder", + true, + bothFunctions, + mockItemType, + "block", + fieldsWithBlockRef, + ); + }); + + it("should pass formatting options to code formatter", async () => { + const customFormatting: Options = { + printWidth: 120, + tabWidth: 4, + singleQuote: true, + }; + + const customConfig = { ...mockConfig, formatting: customFormatting }; + const customGenerator = new FileGenerator( + customConfig, + mockFieldGeneratorFactory, + ); + + await customGenerator.generate(); + + expect(MockedCodeFormatter).toHaveBeenCalledWith(customFormatting); + }); + + it("should handle code formatting errors gracefully", async () => { + mockCodeFormatter.format.mockRejectedValue( + new Error("Formatting failed"), + ); + + await expect(fileGenerator.generate()).rejects.toThrow( + "Formatting failed", + ); + }); + + it("should build correct file content structure", async () => { + const mockImports = "import BlockBuilder from '../../BlockBuilder';"; + const mockFunction = + "export default function buildTest() { return new BlockBuilder(); }"; + + mockImportGenerator.generateImports.mockReturnValue(mockImports); + mockFunctionGenerator.generateFunction.mockReturnValue(mockFunction); + + await fileGenerator.generate(); + + expect(mockCodeFormatter.format).toHaveBeenCalledWith( + `${mockImports}\n\n${mockFunction}`, + ); + }); + }); + + describe("buildFileContent", () => { + it("should properly combine imports and function definition", async () => { + const mockImports = "import statements here"; + const mockFunction = "function definition here"; + + mockImportGenerator.generateImports.mockReturnValue(mockImports); + mockFunctionGenerator.generateFunction.mockReturnValue(mockFunction); + + await fileGenerator.generate(); + + expect(mockCodeFormatter.format).toHaveBeenCalledWith( + "import statements here\n\nfunction definition here", + ); + }); + }); + + describe("integration with item type references", () => { + it("should handle item type references in config", async () => { + const referencedItemType: ItemType = { + id: "referenced-block", + type: "item_type", + name: "Referenced Block", + api_key: "referenced_block", + singleton: false, + sortable: true, + draft_mode_active: false, + all_locales_required: false, + hint: "", + collection_appearance: "table", + modular_block: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_field: null, + inverse_relationships_enabled: false, + title_field: null, + image_preview_field: null, + excerpt_field: null, + workflow: null, + has_singleton_item: false, + name_singular: null, + ordering_meta: null, + } as unknown as ItemType; + + const configWithReferences = { + ...mockConfig, + itemTypeReferences: new Map([["referenced-block", referencedItemType]]), + }; + + const generatorWithRefs = new FileGenerator( + configWithReferences, + mockFieldGeneratorFactory, + ); + + await generatorWithRefs.generate(); + + // The generator should still work normally with references + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockName", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + }); + }); + + describe("edge cases", () => { + it("should handle empty fields array", async () => { + const configWithNoFields = { ...mockConfig, fields: [] }; + const generatorWithNoFields = new FileGenerator( + configWithNoFields, + mockFieldGeneratorFactory, + ); + + await generatorWithNoFields.generate(); + + expect(mockBlockReferenceAnalyzer.getUsedFunctions).toHaveBeenCalledWith( + [], + configWithNoFields.itemTypeReferences, + ); + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockName", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + [], + ); + }); + + it("should handle item type with special characters in name", async () => { + const specialItemType = { + ...mockItemType, + name: "Test Block With Spaces & Special-Chars", + }; + + const configWithSpecialName = { + ...mockConfig, + itemType: specialItemType, + }; + const generatorWithSpecialName = new FileGenerator( + configWithSpecialName, + mockFieldGeneratorFactory, + ); + + await generatorWithSpecialName.generate(); + + // Function name should be processed by toPascalCase + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockWithSpaces&Special-Chars", + "BlockBuilder", + false, + noAsyncFunctions, + specialItemType, + "block", + mockFields, + ); + }); + + it("should handle singleton models", async () => { + const singletonItemType = { + ...mockItemType, + singleton: true, + }; + + const configWithSingleton = { + ...mockConfig, + itemType: singletonItemType, + type: "model" as const, + }; + const generatorWithSingleton = new FileGenerator( + configWithSingleton, + mockFieldGeneratorFactory, + ); + + await generatorWithSingleton.generate(); + + expect(mockFunctionGenerator.generateFunction).toHaveBeenCalledWith( + "buildPascalTestBlockName", + "ModelBuilder", + false, + noAsyncFunctions, + singletonItemType, + "model", + mockFields, + ); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerator.ts b/src/FileGeneration/FileGenerator.ts new file mode 100644 index 0000000..52c060c --- /dev/null +++ b/src/FileGeneration/FileGenerator.ts @@ -0,0 +1,98 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import type { Options } from "prettier"; +import type { FieldGeneratorFactory } from "@/FileGeneration/FieldGenerators/FieldGeneratorFactory"; +import { BlockReferenceAnalyzer } from "@/FileGeneration/FileGenerators/BlockReferenceAnalyzer"; +import { BuilderConfigGenerator } from "@/FileGeneration/FileGenerators/BuilderConfigGenerator"; +import { FieldMethodGenerator } from "@/FileGeneration/FileGenerators/FieldMethodGenerator"; +import { CodeFormatter } from "@/FileGeneration/FileGenerators/formatters/CodeFormatter"; +import { ImportGenerator } from "@/FileGeneration/FileGenerators/ImportGenerator"; +import { toPascalCase } from "@/utils/utils"; +import { FunctionGenerator } from "./FileGenerators/FunctionGenerator"; + +export interface FileGeneratorConfig { + itemType: ItemType; + fields: Field[]; + type: "block" | "model"; + itemTypeReferences: Map; + formatting?: Options; + localDevelopment?: boolean; +} + +/** + * FileGenerator simplified without unnecessary interfaces + * Maintains dependency injection but removes interface overhead + */ +export class FileGenerator { + private readonly importGenerator: ImportGenerator; + private readonly builderConfigGenerator: BuilderConfigGenerator; + private readonly fieldMethodGenerator: FieldMethodGenerator; + private readonly blockReferenceAnalyzer: BlockReferenceAnalyzer; + private readonly functionGenerator: FunctionGenerator; + private readonly codeFormatter: CodeFormatter; + + constructor( + private readonly config: FileGeneratorConfig, + fieldGeneratorFactory: FieldGeneratorFactory, + ) { + this.importGenerator = new ImportGenerator(this.config.localDevelopment); + this.builderConfigGenerator = new BuilderConfigGenerator(); + this.fieldMethodGenerator = new FieldMethodGenerator( + fieldGeneratorFactory, + this.config.itemTypeReferences, + ); + this.blockReferenceAnalyzer = new BlockReferenceAnalyzer(); + this.functionGenerator = new FunctionGenerator( + this.builderConfigGenerator, + this.fieldMethodGenerator, + ); + this.codeFormatter = new CodeFormatter(this.config.formatting); + } + + /** + * Generate a TypeScript file for a block or model + * Simplified to follow KISS principle + */ + public async generate(): Promise { + const context = this.createGenerationContext(); + const content = this.buildFileContent(context); + return this.codeFormatter.format(content); + } + + private createGenerationContext() { + const usedFunctions = this.blockReferenceAnalyzer.getUsedFunctions( + this.config.fields, + this.config.itemTypeReferences, + ); + + return { + builderClass: + this.config.type === "block" ? "BlockBuilder" : "ModelBuilder", + functionName: `build${toPascalCase(this.config.itemType.name)}`, + needsAsync: usedFunctions.needsGetBlock || usedFunctions.needsGetModel, + usedFunctions, + }; + } + + private buildFileContent(context: { + builderClass: string; + functionName: string; + needsAsync: boolean; + usedFunctions: any; + }): string { + const imports = this.importGenerator.generateImports(context.builderClass); + const functionDef = this.functionGenerator.generateFunction( + context.functionName, + context.builderClass, + context.needsAsync, + context.usedFunctions, + this.config.itemType, + this.config.type, + this.config.fields, + ); + + return `${imports}\n\n${functionDef}`; + } +} diff --git a/src/FileGeneration/FileGenerators/BlockReferenceAnalyzer.test.ts b/src/FileGeneration/FileGenerators/BlockReferenceAnalyzer.test.ts new file mode 100644 index 0000000..255110d --- /dev/null +++ b/src/FileGeneration/FileGenerators/BlockReferenceAnalyzer.test.ts @@ -0,0 +1,820 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { BlockReferenceAnalyzer } from "@/FileGeneration/FileGenerators/BlockReferenceAnalyzer"; + +function createMockItemType( + name: string, + modular_block: boolean = false, +): ItemType { + return { + id: `test-${name.toLowerCase()}-id`, + type: "item_type", + name, + api_key: name.toLowerCase().replace(/\s+/g, "_"), + collection_appearance: "table", + singleton: false, + all_locales_required: true, + sortable: false, + modular_block, + draft_mode_active: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_meta: null, + has_singleton_item: false, + hint: null, + inverse_relationships_enabled: false, + singleton_item: null, + fields: [], + fieldsets: [], + presentation_title_field: null, + presentation_image_field: null, + title_field: null, + image_preview_field: null, + excerpt_field: null, + ordering_field: null, + workflow: null, + meta: { has_singleton_item: false }, + } as ItemType; +} + +describe("BlockReferenceAnalyzer", () => { + let analyzer: BlockReferenceAnalyzer; + let baseField: Field; + + beforeEach(() => { + analyzer = new BlockReferenceAnalyzer(); + + baseField = { + id: "test-field-id", + type: "field", + field_type: "string", + label: "Test Field", + api_key: "test_field", + hint: "", + localized: false, + validators: {}, + position: 1, + appearance: { addons: [], editor: "single_line", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "item-type-id", type: "item_type" }, + fieldset: null, + }; + }); + + describe("hasBlockReferences", () => { + it("should return false for empty fields array", () => { + const result = analyzer.hasBlockReferences([]); + expect(result).toBe(false); + }); + + it("should return false for fields without block references", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "string", + validators: {}, + }, + { + ...baseField, + id: "date-field", + field_type: "date", + validators: {}, + }, + { + ...baseField, + id: "integer-field", + field_type: "integer", + validators: {}, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(false); + }); + + it("should return true for rich_text field with block references", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: ["block-1", "block-2"], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + + it("should return true for single_block field with block references", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "single_block", + validators: { + single_block_blocks: { + item_types: ["block-1"], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + + it("should return true for structured_text field with block references", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "structured_text", + validators: { + structured_text_blocks: { + item_types: ["block-1", "block-2", "block-3"], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + + it("should return false for rich_text field without item_types", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "rich_text", + validators: { + rich_text_blocks: {}, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(false); + }); + + it("should return false for rich_text field with empty item_types array", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: [], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(false); + }); + + it("should return true if any field has block references", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "string", + validators: {}, + }, + { + ...baseField, + id: "rich-text-field", + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: ["block-1"], + }, + }, + }, + { + ...baseField, + id: "date-field", + field_type: "date", + validators: {}, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + + it("should handle multiple fields with different block reference types", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: ["rich-block-1"], + }, + }, + }, + { + ...baseField, + id: "single-block-field", + field_type: "single_block", + validators: { + single_block: { + item_types: ["single-block-1"], + }, + }, + }, + { + ...baseField, + id: "structured-text-field", + field_type: "structured_text", + validators: { + structured_text_blocks: { + item_types: ["structured-block-1"], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + }); + + describe("fieldReferencesOtherItems", () => { + it("should return false for non-block field types", () => { + const field: Field = { + ...baseField, + field_type: "string", + validators: {}, + }; + + // Access private method through any for testing + const result = (analyzer as any).fieldReferencesOtherItems(field); + expect(result).toBe(false); + }); + + it("should return false for block field types without validators", () => { + const field: Field = { + ...baseField, + field_type: "rich_text", + validators: {}, + }; + + const result = (analyzer as any).fieldReferencesOtherItems(field); + expect(result).toBe(false); + }); + + it("should return true for rich_text with item_types", () => { + const field: Field = { + ...baseField, + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: ["block-1"], + }, + }, + }; + + const result = (analyzer as any).fieldReferencesOtherItems(field); + expect(result).toBe(true); + }); + }); + + describe("getReferencedItemIds", () => { + it("should return empty array for field without validators", () => { + const field: Field = { + ...baseField, + validators: {}, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual([]); + }); + + it("should extract item_types from rich_text_blocks validator", () => { + const field: Field = { + ...baseField, + validators: { + rich_text_blocks: { + item_types: ["block-1", "block-2"], + }, + }, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual(["block-1", "block-2"]); + }); + + it("should extract item_types from single_block_blocks validator", () => { + const field: Field = { + ...baseField, + validators: { + single_block_blocks: { + item_types: ["single-block-1"], + }, + }, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual(["single-block-1"]); + }); + + it("should extract item_types from structured_text_blocks validator", () => { + const field: Field = { + ...baseField, + validators: { + structured_text_blocks: { + item_types: ["structured-1", "structured-2", "structured-3"], + }, + }, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual(["structured-1", "structured-2", "structured-3"]); + }); + + it("should combine item_types from multiple validators", () => { + const field: Field = { + ...baseField, + validators: { + rich_text_blocks: { + item_types: ["rich-1", "rich-2"], + }, + single_block_blocks: { + item_types: ["single-1"], + }, + structured_text_blocks: { + item_types: ["structured-1"], + }, + }, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual(["rich-1", "rich-2", "single-1", "structured-1"]); + }); + + it("should handle missing item_types in validators", () => { + const field: Field = { + ...baseField, + validators: { + rich_text_blocks: {}, + single_block_blocks: { + item_types: ["single-1"], + }, + structured_text_blocks: {}, + }, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual(["single-1"]); + }); + + it("should handle null/undefined validators", () => { + const field: Field = { + ...baseField, + validators: { + rich_text_blocks: null, + single_block: undefined, + } as any, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual([]); + }); + + it("should handle invalid validator structure", () => { + const field: Field = { + ...baseField, + validators: { + rich_text_blocks: "invalid", + single_block: 123, + } as any, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual([]); + }); + }); + + describe("edge cases", () => { + it("should handle malformed validators gracefully", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "rich_text", + validators: null as any, + }, + { + ...baseField, + id: "malformed-field", + field_type: "single_block", + validators: "invalid" as any, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(false); + }); + + it("should handle undefined validators", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "structured_text", + validators: undefined as any, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(false); + }); + + it("should handle very large item_types arrays", () => { + const largeItemTypes = Array.from( + { length: 1000 }, + (_, i) => `block-${i}`, + ); + const fields: Field[] = [ + { + ...baseField, + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: largeItemTypes, + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + }); + + describe("link field support", () => { + it("should detect link fields with item_item_type validator", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "link", + validators: { + item_item_type: { + item_types: ["model-1"], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + + it("should detect links fields with items_item_type validator", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "links", + validators: { + items_item_type: { + item_types: ["model-1", "model-2"], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + + it("should return false for link fields without item validators", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "link", + validators: { + required: {}, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(false); + }); + + it("should return false for links fields without item validators", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "links", + validators: { + size: { min: 1, max: 5 }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(false); + }); + + it("should extract item_types from item_item_type validator", () => { + const field: Field = { + ...baseField, + field_type: "link", + validators: { + item_item_type: { + item_types: ["model-1", "model-2"], + }, + }, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual(["model-1", "model-2"]); + }); + + it("should extract item_types from items_item_type validator", () => { + const field: Field = { + ...baseField, + field_type: "links", + validators: { + items_item_type: { + item_types: ["block-1", "block-2", "model-1"], + }, + }, + }; + + const result = (analyzer as any).getReferencedItemIds(field); + expect(result).toEqual(["block-1", "block-2", "model-1"]); + }); + + it("should handle mixed field types with link fields", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "string", + validators: {}, + }, + { + ...baseField, + id: "link-field", + field_type: "link", + validators: { + item_item_type: { + item_types: ["model-1"], + }, + }, + }, + { + ...baseField, + id: "links-field", + field_type: "links", + validators: { + items_item_type: { + item_types: ["block-1", "block-2"], + }, + }, + }, + { + ...baseField, + id: "rich-text-field", + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: ["rich-block-1"], + }, + }, + }, + ]; + + const result = analyzer.hasBlockReferences(fields); + expect(result).toBe(true); + }); + }); + + describe("getUsedFunctions", () => { + it("should return needsGetModel true for model references", () => { + const itemTypeReferences = new Map([ + ["model-1", createMockItemType("Test Model")], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "link", + validators: { + item_item_type: { + item_types: ["model-1"], + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetModel).toBe(true); + expect(result.needsGetBlock).toBe(false); + }); + + it("should return needsGetBlock true for block references", () => { + const itemTypeReferences = new Map([ + ["block-1", createMockItemType("Test Block", true)], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "links", + validators: { + items_item_type: { + item_types: ["block-1"], + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetModel).toBe(false); + expect(result.needsGetBlock).toBe(true); + }); + + it("should return both true for mixed references", () => { + const itemTypeReferences = new Map([ + ["model-1", createMockItemType("Test Model")], + ["block-1", createMockItemType("Test Block", true)], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "links", + validators: { + items_item_type: { + item_types: ["model-1", "block-1"], + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetModel).toBe(true); + expect(result.needsGetBlock).toBe(true); + }); + + it("should return both true when no itemTypeReferences provided but has references", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "link", + validators: { + item_item_type: { + item_types: ["some-id"], + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields); + expect(result.needsGetModel).toBe(true); + expect(result.needsGetBlock).toBe(true); + }); + + it("should return both false when no references", () => { + const fields: Field[] = [ + { + ...baseField, + field_type: "string", + validators: {}, + }, + ]; + + const result = analyzer.getUsedFunctions(fields); + expect(result.needsGetModel).toBe(false); + expect(result.needsGetBlock).toBe(false); + }); + + it("should detect structured_text_blocks validator", () => { + const itemTypeReferences = new Map([ + ["block-1", createMockItemType("Test Block", true)], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "structured_text", + validators: { + structured_text_blocks: { + item_types: ["block-1"], + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetBlock).toBe(true); + expect(result.needsGetModel).toBe(false); + }); + + it("should detect structured_text_links validator", () => { + const itemTypeReferences = new Map([ + ["model-1", createMockItemType("Test Model")], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "structured_text", + validators: { + structured_text_links: { + item_types: ["model-1"], + on_publish_with_unpublished_references_strategy: "fail", + on_reference_unpublish_strategy: "delete_references", + on_reference_delete_strategy: "delete_references", + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetModel).toBe(true); + expect(result.needsGetBlock).toBe(false); + }); + + it("should detect both structured_text_blocks and structured_text_links validators", () => { + const itemTypeReferences = new Map([ + ["block-1", createMockItemType("Test Block", true)], + ["model-1", createMockItemType("Test Model")], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "structured_text", + validators: { + structured_text_blocks: { + item_types: ["block-1"], + }, + structured_text_links: { + item_types: ["model-1"], + on_publish_with_unpublished_references_strategy: "fail", + on_reference_unpublish_strategy: "delete_references", + on_reference_delete_strategy: "delete_references", + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetBlock).toBe(true); + expect(result.needsGetModel).toBe(true); + }); + + it("should detect single_block_blocks validator", () => { + const itemTypeReferences = new Map([ + ["block-1", createMockItemType("Test Block", true)], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "single_block", + validators: { + single_block_blocks: { + item_types: ["block-1"], + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetBlock).toBe(true); + expect(result.needsGetModel).toBe(false); + }); + + it("should detect rich_text_blocks validator", () => { + const itemTypeReferences = new Map([ + ["block-1", createMockItemType("Test Block", true)], + ]); + + const fields: Field[] = [ + { + ...baseField, + field_type: "rich_text", + validators: { + rich_text_blocks: { + item_types: ["block-1"], + }, + }, + }, + ]; + + const result = analyzer.getUsedFunctions(fields, itemTypeReferences); + expect(result.needsGetBlock).toBe(true); + expect(result.needsGetModel).toBe(false); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerators/BlockReferenceAnalyzer.ts b/src/FileGeneration/FileGenerators/BlockReferenceAnalyzer.ts new file mode 100644 index 0000000..d40dbe8 --- /dev/null +++ b/src/FileGeneration/FileGenerators/BlockReferenceAnalyzer.ts @@ -0,0 +1,87 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; + +export interface UsedFunctions { + needsGetBlock: boolean; + needsGetModel: boolean; +} + +export class BlockReferenceAnalyzer { + hasBlockReferences(fields: Field[]): boolean { + return fields.some((field) => this.fieldReferencesOtherItems(field)); + } + + getUsedFunctions( + fields: Field[], + itemTypeReferences?: Map, + ): UsedFunctions { + let needsGetBlock = false; + let needsGetModel = false; + + if (!itemTypeReferences) { + // If no references available, assume both might be needed + const hasReferences = this.hasBlockReferences(fields); + return { + needsGetBlock: hasReferences, + needsGetModel: hasReferences, + }; + } + + for (const field of fields) { + if (this.fieldReferencesOtherItems(field)) { + const referencedIds = this.getReferencedItemIds(field); + for (const id of referencedIds) { + const itemType = itemTypeReferences.get(id); + if (itemType) { + if (itemType.modular_block) { + needsGetBlock = true; + } else { + needsGetModel = true; + } + } + } + } + } + + return { needsGetBlock, needsGetModel }; + } + + private fieldReferencesOtherItems(field: Field): boolean { + return ( + (field.field_type === "rich_text" || + field.field_type === "single_block" || + field.field_type === "structured_text" || + field.field_type === "link" || + field.field_type === "links") && + this.getReferencedItemIds(field).length > 0 + ); + } + + private getReferencedItemIds(field: Field): string[] { + const itemIds: string[] = []; + const validators = field.validators as any; + + if (validators?.rich_text_blocks?.item_types) { + itemIds.push(...validators.rich_text_blocks.item_types); + } + if (validators?.single_block_blocks?.item_types) { + itemIds.push(...validators.single_block_blocks.item_types); + } + if (validators?.structured_text_blocks?.item_types) { + itemIds.push(...validators.structured_text_blocks.item_types); + } + if (validators?.structured_text_links?.item_types) { + itemIds.push(...validators.structured_text_links.item_types); + } + if (validators?.item_item_type?.item_types) { + itemIds.push(...validators.item_item_type.item_types); + } + if (validators?.items_item_type?.item_types) { + itemIds.push(...validators.items_item_type.item_types); + } + + return itemIds; + } +} diff --git a/src/FileGeneration/FileGenerators/BuilderConfigGenerator.test.ts b/src/FileGeneration/FileGenerators/BuilderConfigGenerator.test.ts new file mode 100644 index 0000000..fea8a13 --- /dev/null +++ b/src/FileGeneration/FileGenerators/BuilderConfigGenerator.test.ts @@ -0,0 +1,438 @@ +import type { ItemType } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { BuilderConfigGenerator } from "@/FileGeneration/FileGenerators/BuilderConfigGenerator"; + +describe("BuilderConfigGenerator", () => { + let generator: BuilderConfigGenerator; + let baseItemType: ItemType; + + beforeEach(() => { + generator = new BuilderConfigGenerator(); + + baseItemType = { + id: "test-item-type-id", + type: "item_type", + name: "Test Item Type", + api_key: "test_item_type", + singleton: false, + sortable: true, + draft_mode_active: false, + all_locales_required: false, + hint: "Test hint", + collection_appearance: "table", + modular_block: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_field: null, + inverse_relationships_enabled: false, + title_field: null, + image_preview_field: null, + excerpt_field: null, + workflow: null, + has_singleton_item: false, + name_singular: null, + ordering_meta: null, + } as unknown as ItemType; + }); + + describe("generateBuilderConfig for models", () => { + it("should generate correct config for basic model", () => { + const result = generator.generateBuilderConfig(baseItemType, "model"); + + expect(result).toBe(`{ + name: 'Test Item Type', + config, + body: { + api_key: 'test_item_type', + singleton: false, + sortable: true, + draft_mode_active: false, + all_locales_required: false, + }, + }`); + }); + + it("should handle singleton model", () => { + const singletonItemType: ItemType = { + ...baseItemType, + singleton: true, + }; + + const result = generator.generateBuilderConfig( + singletonItemType, + "model", + ); + + expect(result).toContain("singleton: true"); + }); + + it("should handle non-sortable model", () => { + const nonSortableItemType: ItemType = { + ...baseItemType, + sortable: false, + }; + + const result = generator.generateBuilderConfig( + nonSortableItemType, + "model", + ); + + expect(result).toContain("sortable: false"); + }); + + it("should handle draft mode active", () => { + const draftModeItemType: ItemType = { + ...baseItemType, + draft_mode_active: true, + }; + + const result = generator.generateBuilderConfig( + draftModeItemType, + "model", + ); + + expect(result).toContain("draft_mode_active: true"); + }); + + it("should handle all locales required", () => { + const allLocalesItemType: ItemType = { + ...baseItemType, + all_locales_required: true, + }; + + const result = generator.generateBuilderConfig( + allLocalesItemType, + "model", + ); + + expect(result).toContain("all_locales_required: true"); + }); + + it("should handle undefined boolean values as false", () => { + const itemTypeWithUndefined: ItemType = { + ...baseItemType, + singleton: undefined as any, + sortable: undefined as any, + draft_mode_active: undefined as any, + all_locales_required: undefined as any, + }; + + const result = generator.generateBuilderConfig( + itemTypeWithUndefined, + "model", + ); + + expect(result).toContain("singleton: false"); + expect(result).toContain("sortable: false"); + expect(result).toContain("draft_mode_active: false"); + expect(result).toContain("all_locales_required: false"); + }); + + it("should handle null boolean values as false", () => { + const itemTypeWithNull: ItemType = { + ...baseItemType, + singleton: null as any, + sortable: null as any, + draft_mode_active: null as any, + all_locales_required: null as any, + }; + + const result = generator.generateBuilderConfig(itemTypeWithNull, "model"); + + expect(result).toContain("singleton: false"); + expect(result).toContain("sortable: false"); + expect(result).toContain("draft_mode_active: false"); + expect(result).toContain("all_locales_required: false"); + }); + + it("should escape single quotes in model name", () => { + const itemTypeWithQuotes: ItemType = { + ...baseItemType, + name: "Test's Model Name", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithQuotes, + "model", + ); + + expect(result).toContain("name: 'Test\\'s Model Name'"); + }); + + it("should escape single quotes in api_key", () => { + const itemTypeWithQuotes: ItemType = { + ...baseItemType, + api_key: "test's_api_key", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithQuotes, + "model", + ); + + expect(result).toContain("api_key: 'test\\'s_api_key'"); + }); + }); + + describe("generateBuilderConfig for blocks", () => { + it("should generate correct config for basic block", () => { + const result = generator.generateBuilderConfig(baseItemType, "block"); + + expect(result).toBe(`{ + name: 'Test Item Type', + config, + options: { + api_key: 'test_item_type', + hint: 'Test hint', + }, + }`); + }); + + it("should handle empty hint", () => { + const itemTypeWithEmptyHint: ItemType = { + ...baseItemType, + hint: "", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithEmptyHint, + "block", + ); + + expect(result).toContain("hint: ''"); + }); + + it("should handle undefined hint", () => { + const itemTypeWithUndefinedHint: ItemType = { + ...baseItemType, + hint: undefined as any, + }; + + const result = generator.generateBuilderConfig( + itemTypeWithUndefinedHint, + "block", + ); + + expect(result).toContain("hint: ''"); + }); + + it("should handle null hint", () => { + const itemTypeWithNullHint: ItemType = { + ...baseItemType, + hint: null as any, + }; + + const result = generator.generateBuilderConfig( + itemTypeWithNullHint, + "block", + ); + + expect(result).toContain("hint: ''"); + }); + + it("should escape single quotes in block name", () => { + const itemTypeWithQuotes: ItemType = { + ...baseItemType, + name: "Test's Block Name", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithQuotes, + "block", + ); + + expect(result).toContain("name: 'Test\\'s Block Name'"); + }); + + it("should escape single quotes in api_key", () => { + const itemTypeWithQuotes: ItemType = { + ...baseItemType, + api_key: "test's_api_key", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithQuotes, + "block", + ); + + expect(result).toContain("api_key: 'test\\'s_api_key'"); + }); + + it("should escape single quotes in hint", () => { + const itemTypeWithQuotes: ItemType = { + ...baseItemType, + hint: "This is a test's hint", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithQuotes, + "block", + ); + + expect(result).toContain("hint: 'This is a test\\'s hint'"); + }); + + it("should handle multiline hints", () => { + const itemTypeWithMultilineHint: ItemType = { + ...baseItemType, + hint: "Line 1\nLine 2\nLine 3", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithMultilineHint, + "block", + ); + + expect(result).toContain("hint: 'Line 1\nLine 2\nLine 3'"); + }); + + it("should handle hints with special characters", () => { + const itemTypeWithSpecialChars: ItemType = { + ...baseItemType, + hint: "Special chars: !@#$%^&*()[]{}|;:,.<>?", + }; + + const result = generator.generateBuilderConfig( + itemTypeWithSpecialChars, + "block", + ); + + expect(result).toContain("hint: 'Special chars: !@#$%^&*()[]{}|;:,.<>?'"); + }); + }); + + describe("configuration structure validation", () => { + it("should always include required model fields", () => { + const result = generator.generateBuilderConfig(baseItemType, "model"); + + expect(result).toContain("name:"); + expect(result).toContain("config,"); + expect(result).toContain("body:"); + expect(result).toContain("api_key:"); + expect(result).toContain("singleton:"); + expect(result).toContain("sortable:"); + expect(result).toContain("draft_mode_active:"); + expect(result).toContain("all_locales_required:"); + }); + + it("should always include required block fields", () => { + const result = generator.generateBuilderConfig(baseItemType, "block"); + + expect(result).toContain("name:"); + expect(result).toContain("config,"); + expect(result).toContain("options:"); + expect(result).toContain("api_key:"); + expect(result).toContain("hint:"); + }); + + it("should not include model-specific fields in block config", () => { + const result = generator.generateBuilderConfig(baseItemType, "block"); + + expect(result).not.toContain("singleton:"); + expect(result).not.toContain("sortable:"); + expect(result).not.toContain("draft_mode_active:"); + expect(result).not.toContain("all_locales_required:"); + expect(result).not.toContain("body:"); + }); + + it("should not include block-specific fields in model config", () => { + const result = generator.generateBuilderConfig(baseItemType, "model"); + + expect(result).not.toContain("hint:"); + expect(result).not.toContain("options:"); + }); + + it("should maintain proper JavaScript object syntax", () => { + const modelResult = generator.generateBuilderConfig( + baseItemType, + "model", + ); + const blockResult = generator.generateBuilderConfig( + baseItemType, + "block", + ); + + // Should start and end with braces + expect(modelResult.trim()).toMatch(/^{[\s\S]*}$/); + expect(blockResult.trim()).toMatch(/^{[\s\S]*}$/); + + // Should have proper comma usage + expect(modelResult).toContain("name: 'Test Item Type',"); + expect(modelResult).toContain("config,"); + expect(blockResult).toContain("name: 'Test Item Type',"); + expect(blockResult).toContain("config,"); + }); + }); + + describe("edge cases", () => { + it("should handle extremely long names", () => { + const longName = "A".repeat(1000); + const itemTypeWithLongName: ItemType = { + ...baseItemType, + name: longName, + }; + + const result = generator.generateBuilderConfig( + itemTypeWithLongName, + "block", + ); + + expect(result).toContain(`name: '${longName}'`); + }); + + it("should handle extremely long api_keys", () => { + const longApiKey = "a".repeat(1000); + const itemTypeWithLongApiKey: ItemType = { + ...baseItemType, + api_key: longApiKey, + }; + + const result = generator.generateBuilderConfig( + itemTypeWithLongApiKey, + "model", + ); + + expect(result).toContain(`api_key: '${longApiKey}'`); + }); + + it("should handle extremely long hints", () => { + const longHint = "This is a very long hint. ".repeat(100); + const itemTypeWithLongHint: ItemType = { + ...baseItemType, + hint: longHint, + }; + + const result = generator.generateBuilderConfig( + itemTypeWithLongHint, + "block", + ); + + expect(result).toContain(`hint: '${longHint}'`); + }); + + it("should handle empty strings", () => { + const itemTypeWithEmptyStrings: ItemType = { + ...baseItemType, + name: "", + api_key: "", + hint: "", + }; + + const modelResult = generator.generateBuilderConfig( + itemTypeWithEmptyStrings, + "model", + ); + const blockResult = generator.generateBuilderConfig( + itemTypeWithEmptyStrings, + "block", + ); + + expect(modelResult).toContain("name: ''"); + expect(modelResult).toContain("api_key: ''"); + expect(blockResult).toContain("name: ''"); + expect(blockResult).toContain("api_key: ''"); + expect(blockResult).toContain("hint: ''"); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerators/BuilderConfigGenerator.ts b/src/FileGeneration/FileGenerators/BuilderConfigGenerator.ts new file mode 100644 index 0000000..e79a261 --- /dev/null +++ b/src/FileGeneration/FileGenerators/BuilderConfigGenerator.ts @@ -0,0 +1,46 @@ +import type { ItemType } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +export class BuilderConfigGenerator { + generateBuilderConfig(itemType: ItemType, type: "block" | "model"): string { + const baseConfig = this.generateBaseConfig(itemType); + const typeSpecificConfig = + type === "model" + ? this.generateModelConfig(itemType) + : this.generateBlockConfig(itemType); + + return `{${baseConfig}${typeSpecificConfig} + }`; + } + + private generateBaseConfig(itemType: ItemType): string { + return ` + name: '${this.escapeString(itemType.name)}', + config,`; + } + + private generateModelConfig(itemType: ItemType): string { + return ` + body: { + api_key: '${this.escapeString(itemType.api_key)}', + singleton: ${this.normalizeBooleanValue(itemType.singleton)}, + sortable: ${this.normalizeBooleanValue(itemType.sortable)}, + draft_mode_active: ${this.normalizeBooleanValue(itemType.draft_mode_active)}, + all_locales_required: ${this.normalizeBooleanValue(itemType.all_locales_required)}, + },`; + } + + private generateBlockConfig(itemType: ItemType): string { + return ` + options: { + api_key: '${this.escapeString(itemType.api_key)}', + hint: '${this.escapeString(itemType.hint || "")}', + },`; + } + + private normalizeBooleanValue(value: boolean | undefined | null): boolean { + return Boolean(value); + } + + private escapeString(value: string): string { + return value.replace(/'/g, "\\'").replace(/"/g, '\\"'); + } +} diff --git a/src/FileGeneration/FileGenerators/FieldMethodGenerator.test.ts b/src/FileGeneration/FileGenerators/FieldMethodGenerator.test.ts new file mode 100644 index 0000000..0bb282b --- /dev/null +++ b/src/FileGeneration/FileGenerators/FieldMethodGenerator.test.ts @@ -0,0 +1,477 @@ +import type { Field } from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import type { FieldGenerator } from "@/FileGeneration/FieldGenerators/FieldGenerator"; +import { FieldGeneratorFactory } from "@/FileGeneration/FieldGenerators/FieldGeneratorFactory"; +import { FieldMethodGenerator } from "@/FileGeneration/FileGenerators/FieldMethodGenerator"; + +// Mock the FieldGeneratorFactory +jest.mock("@/FileGeneration/FieldGenerators/FieldGeneratorFactory"); + +const MockedFieldGeneratorFactory = FieldGeneratorFactory as jest.MockedClass< + typeof FieldGeneratorFactory +>; + +describe("FieldMethodGenerator", () => { + let generator: FieldMethodGenerator; + let mockFieldGeneratorFactory: jest.Mocked; + let baseField: Field; + + beforeEach(() => { + jest.clearAllMocks(); + + mockFieldGeneratorFactory = { + createGenerator: jest.fn(), + } as any; + + MockedFieldGeneratorFactory.mockImplementation( + () => mockFieldGeneratorFactory, + ); + + generator = new FieldMethodGenerator(mockFieldGeneratorFactory); + + baseField = { + id: "test-field-id", + type: "field", + label: "Test Field", + field_type: "string", + api_key: "test_field", + hint: "", + localized: false, + validators: {}, + position: 1 as number, + appearance: { addons: [], editor: "single_line", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: "item-type-id", type: "item_type" }, + fieldset: null, + }; + }); + + describe("generateFieldMethods", () => { + it("should return empty string for empty fields array", () => { + const result = generator.generateFieldMethods([]); + + expect(result).toBe(""); + expect(mockFieldGeneratorFactory.createGenerator).not.toHaveBeenCalled(); + }); + + it("should generate method for single field", () => { + const mockGenerator: jest.Mocked> = { + generateMethodCall: jest + .fn() + .mockReturnValue(".addText({ label: 'Test Field' })"), + } as any; + + mockFieldGeneratorFactory.createGenerator.mockReturnValue(mockGenerator); + + const fields = [baseField]; + const result = generator.generateFieldMethods(fields); + + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenCalledTimes( + 1, + ); + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenCalledWith({ + field: baseField, + }); + expect(mockGenerator.generateMethodCall).toHaveBeenCalledTimes(1); + expect(result).toBe(".addText({ label: 'Test Field' })"); + }); + + it("should generate methods for multiple fields", () => { + const field1 = { ...baseField, position: 2 }; + const field2 = { ...baseField, id: "field-2", position: 1 }; + const field3 = { ...baseField, id: "field-3", position: 3 }; + + const mockGenerator1: jest.Mocked> = { + generateMethodCall: jest + .fn() + .mockReturnValue(".addText({ label: 'Field 1' })"), + } as any; + const mockGenerator2: jest.Mocked> = { + generateMethodCall: jest + .fn() + .mockReturnValue(".addText({ label: 'Field 2' })"), + } as any; + const mockGenerator3: jest.Mocked> = { + generateMethodCall: jest + .fn() + .mockReturnValue(".addText({ label: 'Field 3' })"), + } as any; + + mockFieldGeneratorFactory.createGenerator + .mockReturnValueOnce(mockGenerator2) // field2 (position 1) + .mockReturnValueOnce(mockGenerator1) // field1 (position 2) + .mockReturnValueOnce(mockGenerator3); // field3 (position 3) + + const fields = [field1, field2, field3]; + const result = generator.generateFieldMethods(fields); + + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenCalledTimes( + 3, + ); + expect(result).toBe( + ".addText({ label: 'Field 2' }).addText({ label: 'Field 1' }).addText({ label: 'Field 3' })", + ); + }); + + it("should sort fields by position", () => { + const field1 = { + ...baseField, + id: "field-1", + position: 3, + label: "Third Field", + }; + const field2 = { + ...baseField, + id: "field-2", + position: 1, + label: "First Field", + }; + const field3 = { + ...baseField, + id: "field-3", + position: 2, + label: "Second Field", + }; + + const mockGenerator1: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".addThird()"), + } as any; + const mockGenerator2: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".addFirst()"), + } as any; + const mockGenerator3: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".addSecond()"), + } as any; + + // The factory should be called in position order (1, 2, 3) + mockFieldGeneratorFactory.createGenerator + .mockReturnValueOnce(mockGenerator2) // position 1 + .mockReturnValueOnce(mockGenerator3) // position 2 + .mockReturnValueOnce(mockGenerator1); // position 3 + + const fields = [field1, field2, field3]; // Unsorted input + const result = generator.generateFieldMethods(fields); + + // Check that createGenerator was called in position order + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 1, + { field: field2 }, + ); + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 2, + { field: field3 }, + ); + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 3, + { field: field1 }, + ); + + expect(result).toBe(".addFirst().addSecond().addThird()"); + }); + + it("should handle fields with undefined positions", () => { + const field1 = { + ...baseField, + id: "field-1", + position: undefined as any, + label: "Field 1", + }; + const field2 = { + ...baseField, + id: "field-2", + position: 2, + label: "Field 2", + }; + const field3 = { + ...baseField, + id: "field-3", + position: undefined as any, + label: "Field 3", + }; + + const mockGenerator1: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add1()"), + } as any; + const mockGenerator2: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add2()"), + } as any; + const mockGenerator3: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add3()"), + } as any; + + mockFieldGeneratorFactory.createGenerator + .mockReturnValueOnce(mockGenerator1) // undefined position (treated as 0) + .mockReturnValueOnce(mockGenerator3) // undefined position (treated as 0) + .mockReturnValueOnce(mockGenerator2); // position 2 + + const fields = [field1, field2, field3]; + const result = generator.generateFieldMethods(fields); + + expect(result).toBe(".add1().add3().add2()"); + }); + + it("should handle fields with null positions", () => { + const field1 = { + ...baseField, + id: "field-1", + position: null as any, + label: "Field 1", + }; + const field2 = { + ...baseField, + id: "field-2", + position: 1, + label: "Field 2", + }; + + const mockGenerator1: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add1()"), + } as any; + const mockGenerator2: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add2()"), + } as any; + + mockFieldGeneratorFactory.createGenerator + .mockReturnValueOnce(mockGenerator1) // null position (treated as 0) + .mockReturnValueOnce(mockGenerator2); // position 1 + + const fields = [field1, field2]; + const result = generator.generateFieldMethods(fields); + + expect(result).toBe(".add1().add2()"); + }); + + it("should handle fields with same positions", () => { + const field1 = { + ...baseField, + id: "field-1", + position: 1, + label: "Field 1", + }; + const field2 = { + ...baseField, + id: "field-2", + position: 1, + label: "Field 2", + }; + const field3 = { + ...baseField, + id: "field-3", + position: 1, + label: "Field 3", + }; + + const mockGenerator1: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add1()"), + } as any; + const mockGenerator2: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add2()"), + } as any; + const mockGenerator3: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add3()"), + } as any; + + mockFieldGeneratorFactory.createGenerator + .mockReturnValueOnce(mockGenerator1) + .mockReturnValueOnce(mockGenerator2) + .mockReturnValueOnce(mockGenerator3); + + const fields = [field1, field2, field3]; + const result = generator.generateFieldMethods(fields); + + // Should maintain the original order when positions are the same + expect(result).toBe(".add1().add2().add3()"); + }); + + it("should not modify the original fields array", () => { + const field1 = { ...baseField, id: "field-1", position: 3 }; + const field2 = { ...baseField, id: "field-2", position: 1 }; + const originalFields = [field1, field2]; + const originalFieldsCopy = [...originalFields]; + + const mockGenerator: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add()"), + } as any; + + mockFieldGeneratorFactory.createGenerator.mockReturnValue(mockGenerator); + + generator.generateFieldMethods(originalFields); + + // Original array should remain unchanged + expect(originalFields).toEqual(originalFieldsCopy); + expect(originalFields[0]).toBe(field1); + expect(originalFields[1]).toBe(field2); + }); + + it("should handle complex field configurations", () => { + const richTextField: Field = { + ...baseField, + id: "rich-text-field", + field_type: "rich_text", + position: 1, + validators: { + rich_text_blocks: { + item_types: ["block-1", "block-2"], + }, + }, + }; + + const dateField: Field = { + ...baseField, + id: "date-field", + field_type: "date", + position: 2, + validators: { + required: {}, + date_range: { + min: "2023-01-01", + max: "2025-12-31", + }, + }, + }; + + const mockRichTextGenerator: jest.Mocked> = { + generateMethodCall: jest + .fn() + .mockReturnValue( + ".addRichText({ label: 'Rich Text', body: { blocks: ['block-1', 'block-2'] } })", + ), + } as any; + + const mockDateGenerator: jest.Mocked> = { + generateMethodCall: jest + .fn() + .mockReturnValue( + ".addDate({ label: 'Date', body: { required: true, min: '2023-01-01', max: '2025-12-31' } })", + ), + } as any; + + mockFieldGeneratorFactory.createGenerator + .mockReturnValueOnce(mockRichTextGenerator) + .mockReturnValueOnce(mockDateGenerator); + + const fields = [richTextField, dateField]; + const result = generator.generateFieldMethods(fields); + + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenCalledWith({ + field: richTextField, + }); + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenCalledWith({ + field: dateField, + }); + expect(result).toBe( + ".addRichText({ label: 'Rich Text', body: { blocks: ['block-1', 'block-2'] } })" + + ".addDate({ label: 'Date', body: { required: true, min: '2023-01-01', max: '2025-12-31' } })", + ); + }); + + it("should handle generator errors gracefully", () => { + const mockGenerator: jest.Mocked> = { + generateMethodCall: jest.fn().mockImplementation(() => { + throw new Error("Generator failed"); + }), + } as any; + + mockFieldGeneratorFactory.createGenerator.mockReturnValue(mockGenerator); + + const fields = [baseField]; + + expect(() => generator.generateFieldMethods(fields)).toThrow( + "Generator failed", + ); + }); + + it("should handle factory creation errors gracefully", () => { + mockFieldGeneratorFactory.createGenerator.mockImplementation(() => { + throw new Error("Factory failed"); + }); + + const fields = [baseField]; + + expect(() => generator.generateFieldMethods(fields)).toThrow( + "Factory failed", + ); + }); + }); + + describe("edge cases", () => { + it("should handle very large number of fields", () => { + const fields = Array.from({ length: 1000 }, (_, index) => ({ + ...baseField, + id: `field-${index}`, + position: index, + })); + + const mockGenerator: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add()"), + } as any; + + mockFieldGeneratorFactory.createGenerator.mockReturnValue(mockGenerator); + + const result = generator.generateFieldMethods(fields); + + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenCalledTimes( + 1000, + ); + expect(result).toBe(".add()".repeat(1000)); + }); + + it("should handle negative positions", () => { + const field1 = { ...baseField, id: "field-1", position: -1 }; + const field2 = { ...baseField, id: "field-2", position: 0 }; + const field3 = { ...baseField, id: "field-3", position: 1 }; + + const mockGenerator: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add()"), + } as any; + + mockFieldGeneratorFactory.createGenerator.mockReturnValue(mockGenerator); + + const fields = [field1, field2, field3]; + generator.generateFieldMethods(fields); + + // Should be called in order: -1, 0, 1 + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 1, + { field: field1 }, + ); + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 2, + { field: field2 }, + ); + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 3, + { field: field3 }, + ); + }); + + it("should handle very large position numbers", () => { + const field1 = { + ...baseField, + id: "field-1", + position: Number.MAX_SAFE_INTEGER, + }; + const field2 = { ...baseField, id: "field-2", position: 1 }; + + const mockGenerator: jest.Mocked> = { + generateMethodCall: jest.fn().mockReturnValue(".add()"), + } as any; + + mockFieldGeneratorFactory.createGenerator.mockReturnValue(mockGenerator); + + const fields = [field1, field2]; + generator.generateFieldMethods(fields); + + // Should be called in order: 1, MAX_SAFE_INTEGER + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 1, + { field: field2 }, + ); + expect(mockFieldGeneratorFactory.createGenerator).toHaveBeenNthCalledWith( + 2, + { field: field1 }, + ); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerators/FieldMethodGenerator.ts b/src/FileGeneration/FileGenerators/FieldMethodGenerator.ts new file mode 100644 index 0000000..482f53c --- /dev/null +++ b/src/FileGeneration/FileGenerators/FieldMethodGenerator.ts @@ -0,0 +1,38 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import type { FieldGeneratorFactory } from "@/FileGeneration/FieldGenerators/FieldGeneratorFactory"; +export class FieldMethodGenerator { + constructor( + private readonly fieldGeneratorFactory: FieldGeneratorFactory, + private readonly itemTypeReferences?: Map, + ) {} + + public generateFieldMethods(fields: Field[]): string { + if (!fields) { + throw new Error("Invalid input: fields cannot be null or undefined"); + } + + if (fields.length === 0) { + return ""; + } + + const sortedFields = [...fields].sort( + (a, b) => (a.position || 0) - (b.position || 0), + ); + return this.generateMethodCalls(sortedFields); + } + + private generateMethodCalls(fields: Field[]): string { + return fields.map((field) => this.generateSingleMethodCall(field)).join(""); + } + + private generateSingleMethodCall(field: Field): string { + const generator = this.fieldGeneratorFactory.createGenerator({ + field, + itemTypeReferences: this.itemTypeReferences, + }); + return generator.generateMethodCall(); + } +} diff --git a/src/FileGeneration/FileGenerators/FunctionGenerator.test.ts b/src/FileGeneration/FileGenerators/FunctionGenerator.test.ts new file mode 100644 index 0000000..fe16958 --- /dev/null +++ b/src/FileGeneration/FileGenerators/FunctionGenerator.test.ts @@ -0,0 +1,610 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import type { UsedFunctions } from "@/FileGeneration/FileGenerators/BlockReferenceAnalyzer"; +import { BuilderConfigGenerator } from "@/FileGeneration/FileGenerators/BuilderConfigGenerator"; +import { FieldMethodGenerator } from "@/FileGeneration/FileGenerators/FieldMethodGenerator"; +import { FunctionGenerator } from "@/FileGeneration/FileGenerators/FunctionGenerator"; + +// Mock the dependencies +jest.mock("@/FileGeneration/FileGenerators/BuilderConfigGenerator"); +jest.mock("@/FileGeneration/FileGenerators/FieldMethodGenerator"); + +const MockedBuilderConfigGenerator = BuilderConfigGenerator as jest.MockedClass< + typeof BuilderConfigGenerator +>; +const MockedFieldMethodGenerator = FieldMethodGenerator as jest.MockedClass< + typeof FieldMethodGenerator +>; + +describe("FunctionGenerator", () => { + let generator: FunctionGenerator; + let mockBuilderConfigGenerator: jest.Mocked; + let mockFieldMethodGenerator: jest.Mocked; + let mockItemType: ItemType; + let mockFields: Field[]; + let noAsyncFunctions: UsedFunctions; + let bothFunctions: UsedFunctions; + let onlyGetModel: UsedFunctions; + let onlyGetBlock: UsedFunctions; + + beforeEach(() => { + jest.clearAllMocks(); + + // Initialize usedFunctions scenarios + noAsyncFunctions = { needsGetModel: false, needsGetBlock: false }; + bothFunctions = { needsGetModel: true, needsGetBlock: true }; + onlyGetModel = { needsGetModel: true, needsGetBlock: false }; + onlyGetBlock = { needsGetModel: false, needsGetBlock: true }; + + mockBuilderConfigGenerator = { + generateBuilderConfig: jest.fn(), + } as any; + + mockFieldMethodGenerator = { + generateFieldMethods: jest.fn(), + } as any; + + MockedBuilderConfigGenerator.mockImplementation( + () => mockBuilderConfigGenerator, + ); + MockedFieldMethodGenerator.mockImplementation( + () => mockFieldMethodGenerator, + ); + + generator = new FunctionGenerator( + mockBuilderConfigGenerator, + mockFieldMethodGenerator, + ); + + mockItemType = { + id: "test-item-type-id", + type: "item_type", + name: "Test Block", + api_key: "test_block", + singleton: false, + sortable: true, + draft_mode_active: false, + all_locales_required: false, + hint: "Test hint", + collection_appearance: "table", + modular_block: false, + draft_saving_active: false, + tree: false, + ordering_direction: null, + ordering_field: null, + inverse_relationships_enabled: false, + title_field: null, + image_preview_field: null, + excerpt_field: null, + workflow: null, + has_singleton_item: false, + name_singular: null, + ordering_meta: null, + } as unknown as ItemType; + + mockFields = [ + { + id: "field-1", + type: "field", + label: "Test Field", + field_type: "string", + api_key: "test_field", + hint: "", + localized: false, + validators: {}, + position: 1, + appearance: { addons: [], editor: "single_line", parameters: {} }, + default_value: null, + deep_filtering_enabled: false, + item_type: { id: mockItemType.id, type: "item_type" }, + fieldset: null, + }, + ]; + + // Set up default mock return values + mockBuilderConfigGenerator.generateBuilderConfig.mockReturnValue(`{ + name: 'Test Block', + config, + options: { api_key: 'test_block' } + }`); + + mockFieldMethodGenerator.generateFieldMethods.mockReturnValue( + ".addText({ label: 'Test Field' })", + ); + }); + + describe("generateFunction", () => { + it("should generate synchronous function for block without async needs", () => { + const result = generator.generateFunction( + "buildTestBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain( + "export default function buildTestBlock({ config }: BuilderContext)", + ); + expect(result).not.toContain("async"); + expect(result).not.toContain("getBlock"); + expect(result).not.toContain("getModel"); + expect( + mockBuilderConfigGenerator.generateBuilderConfig, + ).toHaveBeenCalledWith(mockItemType, "block"); + expect( + mockFieldMethodGenerator.generateFieldMethods, + ).toHaveBeenCalledWith(mockFields); + }); + + it("should generate asynchronous function for block with both functions", () => { + const result = generator.generateFunction( + "buildTestBlock", + "BlockBuilder", + true, + bothFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain( + "export default async function buildTestBlock({ config, getBlock, getModel }: BuilderContext)", + ); + expect(result).toContain("async"); + }); + + it("should generate asynchronous function with only getModel", () => { + const result = generator.generateFunction( + "buildTestBlock", + "BlockBuilder", + true, + onlyGetModel, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain( + "export default async function buildTestBlock({ config, getModel }: BuilderContext)", + ); + expect(result).toContain("async"); + expect(result).toContain("getModel"); + expect(result).not.toContain("getBlock"); + }); + + it("should generate asynchronous function with only getBlock", () => { + const result = generator.generateFunction( + "buildTestBlock", + "BlockBuilder", + true, + onlyGetBlock, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain( + "export default async function buildTestBlock({ config, getBlock }: BuilderContext)", + ); + expect(result).toContain("async"); + expect(result).toContain("getBlock"); + expect(result).not.toContain("getModel"); + }); + + it("should generate function for model type", () => { + const result = generator.generateFunction( + "buildTestModel", + "ModelBuilder", + false, + noAsyncFunctions, + mockItemType, + "model", + mockFields, + ); + + expect(result).toContain( + "export default function buildTestModel({ config }: BuilderContext)", + ); + expect(result).toContain("return new ModelBuilder"); + expect( + mockBuilderConfigGenerator.generateBuilderConfig, + ).toHaveBeenCalledWith(mockItemType, "model"); + }); + + it("should include proper JSDoc comment with item type information", () => { + // Mock Date to return consistent timestamp + const mockDate = new Date("2024-07-15T10:30:00.000Z"); + jest.spyOn(global, "Date").mockImplementation(() => mockDate as any); + + const result = generator.generateFunction( + "buildTestBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain( + '/**\n * Build a "Test Block" block in DatoCMS.', + ); + expect(result).toContain( + "Generated from DatoCMS API on 2024-07-15T10:30:00.000Z", + ); + expect(result).toContain("API Key: test_block"); + expect(result).toContain(" */"); + + jest.restoreAllMocks(); + }); + + it("should include proper JSDoc comment for model", () => { + const mockDate = new Date("2024-07-15T10:30:00.000Z"); + jest.spyOn(global, "Date").mockImplementation(() => mockDate as any); + + const result = generator.generateFunction( + "buildTestModel", + "ModelBuilder", + false, + noAsyncFunctions, + mockItemType, + "model", + mockFields, + ); + + expect(result).toContain( + '/**\n * Build a "Test Block" model in DatoCMS.', + ); + expect(result).toContain( + "Generated from DatoCMS API on 2024-07-15T10:30:00.000Z", + ); + expect(result).toContain("API Key: test_block"); + + jest.restoreAllMocks(); + }); + + it("should properly combine builder config and field methods", () => { + const builderConfig = `{ + name: 'Custom Block', + config, + options: { api_key: 'custom_block' } + }`; + + const fieldMethods = + ".addText({ label: 'Field 1' }).addDate({ label: 'Field 2' })"; + + mockBuilderConfigGenerator.generateBuilderConfig.mockReturnValue( + builderConfig, + ); + mockFieldMethodGenerator.generateFieldMethods.mockReturnValue( + fieldMethods, + ); + + const result = generator.generateFunction( + "buildCustomBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain( + `return new BlockBuilder(${builderConfig})${fieldMethods};`, + ); + }); + + it("should handle empty field methods", () => { + mockFieldMethodGenerator.generateFieldMethods.mockReturnValue(""); + + const result = generator.generateFunction( + "buildEmptyBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + [], + ); + + expect(result).toContain("return new BlockBuilder({"); + expect(result).toContain("});"); + }); + + it("should handle complex field methods", () => { + const complexFieldMethods = ` + .addText({ label: 'Title', body: { required: true } }) + .addRichText({ label: 'Content', body: { blocks: ['image', 'video'] } }) + .addDate({ label: 'Published At', body: { min: '2023-01-01' } }) + `; + + mockFieldMethodGenerator.generateFieldMethods.mockReturnValue( + complexFieldMethods, + ); + + const result = generator.generateFunction( + "buildComplexBlock", + "BlockBuilder", + true, + bothFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain("return new BlockBuilder({"); + expect(result).toContain(`})${complexFieldMethods};`); + }); + + it("should handle special characters in item type name", () => { + const specialItemType = { + ...mockItemType, + name: "Test Block with \"quotes\" and 'apostrophes'", + }; + + const result = generator.generateFunction( + "buildSpecialBlock", + "BlockBuilder", + false, + noAsyncFunctions, + specialItemType, + "block", + mockFields, + ); + + expect(result).toContain( + "Build a \"Test Block with \\\"quotes\\\" and \\''apostrophes\\''\"", + ); + }); + + it("should handle multiline builder config", () => { + const multilineConfig = `{ + name: 'Multi Line Block', + config, + options: { + api_key: 'multi_line_block', + hint: 'This is a very long hint that might span multiple lines' + } + }`; + + mockBuilderConfigGenerator.generateBuilderConfig.mockReturnValue( + multilineConfig, + ); + + const result = generator.generateFunction( + "buildMultiLineBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain(`return new BlockBuilder(${multilineConfig})`); + }); + + it("should generate proper function signature for sync block", () => { + const result = generator.generateFunction( + "buildSyncBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toMatch( + /export default function buildSyncBlock\(\{ config \}: BuilderContext\) \{/, + ); + }); + + it("should generate proper function signature for async block", () => { + const result = generator.generateFunction( + "buildAsyncBlock", + "BlockBuilder", + true, + bothFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toMatch( + /export default async function buildAsyncBlock\(\{ config, getBlock, getModel \}: BuilderContext\) \{/, + ); + }); + + it("should generate proper function signature for sync model", () => { + const result = generator.generateFunction( + "buildSyncModel", + "ModelBuilder", + false, + noAsyncFunctions, + mockItemType, + "model", + mockFields, + ); + + expect(result).toMatch( + /export default function buildSyncModel\(\{ config \}: BuilderContext\) \{/, + ); + }); + + it("should generate proper function signature for async model", () => { + const result = generator.generateFunction( + "buildAsyncModel", + "ModelBuilder", + true, + bothFunctions, + mockItemType, + "model", + mockFields, + ); + + expect(result).toMatch( + /export default async function buildAsyncModel\(\{ config, getBlock, getModel \}: BuilderContext\) \{/, + ); + }); + }); + + describe("error handling", () => { + it("should propagate errors from builder config generator", () => { + mockBuilderConfigGenerator.generateBuilderConfig.mockImplementation( + () => { + throw new Error("Builder config failed"); + }, + ); + + expect(() => { + generator.generateFunction( + "buildErrorBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + }).toThrow("Builder config failed"); + }); + + it("should propagate errors from field method generator", () => { + mockFieldMethodGenerator.generateFieldMethods.mockImplementation(() => { + throw new Error("Field methods failed"); + }); + + expect(() => { + generator.generateFunction( + "buildErrorBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + }).toThrow("Field methods failed"); + }); + }); + + describe("edge cases", () => { + it("should handle very long function names", () => { + const longFunctionName = "build" + "A".repeat(1000) + "Block"; + + const result = generator.generateFunction( + longFunctionName, + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain(`export default function ${longFunctionName}(`); + }); + + it("should handle empty item type name", () => { + const emptyNameItemType = { + ...mockItemType, + name: "", + }; + + const result = generator.generateFunction( + "buildEmptyNameBlock", + "BlockBuilder", + false, + noAsyncFunctions, + emptyNameItemType, + "block", + mockFields, + ); + + expect(result).toContain('Build a ""'); + }); + + it("should handle empty api_key", () => { + const emptyApiKeyItemType = { + ...mockItemType, + api_key: "", + }; + + const result = generator.generateFunction( + "buildEmptyApiKeyBlock", + "BlockBuilder", + false, + noAsyncFunctions, + emptyApiKeyItemType, + "block", + mockFields, + ); + + expect(result).toContain("API Key: "); + }); + + it("should handle very large fields array", () => { + const largeFieldsArray = Array.from({ length: 1000 }, (_, index) => ({ + ...mockFields[0], + id: `field-${index}`, + type: "field" as const, + label: `Field ${index}`, + field_type: "string" as const, + localized: false, + default_value: null, + hint: "", + validators: {}, + position: index + 1, + api_key: `field_${index}`, + appearance: { addons: [], editor: "single_line", parameters: {} }, + deep_filtering_enabled: false, + item_type: { id: mockItemType.id, type: "item_type" as const }, + fieldset: null, + })); + + const result = generator.generateFunction( + "buildLargeBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + largeFieldsArray, + ); + + expect( + mockFieldMethodGenerator.generateFieldMethods, + ).toHaveBeenCalledWith(largeFieldsArray); + expect(result).toContain("export default function buildLargeBlock"); + }); + + it("should maintain consistent timestamp format", () => { + const fixedDate = new Date("2024-12-25T15:30:45.123Z"); + jest.spyOn(global, "Date").mockImplementation(() => fixedDate as any); + + const result = generator.generateFunction( + "buildTimestampBlock", + "BlockBuilder", + false, + noAsyncFunctions, + mockItemType, + "block", + mockFields, + ); + + expect(result).toContain( + "Generated from DatoCMS API on 2024-12-25T15:30:45.123Z", + ); + + jest.restoreAllMocks(); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerators/FunctionGenerator.ts b/src/FileGeneration/FileGenerators/FunctionGenerator.ts new file mode 100644 index 0000000..cd644a9 --- /dev/null +++ b/src/FileGeneration/FileGenerators/FunctionGenerator.ts @@ -0,0 +1,99 @@ +import type { + Field, + ItemType, +} from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import type { UsedFunctions } from "@/FileGeneration/FileGenerators/BlockReferenceAnalyzer"; +import type { BuilderConfigGenerator } from "@/FileGeneration/FileGenerators/BuilderConfigGenerator"; +import type { FieldMethodGenerator } from "@/FileGeneration/FileGenerators/FieldMethodGenerator"; + +export class FunctionGenerator { + constructor( + private readonly builderConfigGenerator: BuilderConfigGenerator, + private readonly fieldMethodGenerator: FieldMethodGenerator, + ) {} + + generateFunction( + functionName: string, + builderClass: string, + needsAsync: boolean, + usedFunctions: UsedFunctions, + itemType: ItemType, + type: "block" | "model", + fields: Field[], + ): string { + this.validateInputs(functionName, builderClass, itemType); + + const functionSignature = this.createFunctionSignature( + functionName, + needsAsync, + usedFunctions, + ); + const documentation = this.createDocumentation(itemType, type); + const builderConfig = this.builderConfigGenerator.generateBuilderConfig( + itemType, + type, + ); + const fieldMethods = this.fieldMethodGenerator.generateFieldMethods(fields); + const functionBody = this.createFunctionBody( + builderClass, + builderConfig, + fieldMethods, + ); + + return `${documentation} +${functionSignature} { + ${functionBody} +}`; + } + + private validateInputs( + functionName: string, + builderClass: string, + itemType: ItemType, + ): void { + if (!functionName) + throw new Error("functionName cannot be null or undefined"); + if (!builderClass) + throw new Error("builderClass cannot be null or undefined"); + if (!itemType) throw new Error("itemType cannot be null or undefined"); + } + + private createFunctionSignature( + functionName: string, + needsAsync: boolean, + usedFunctions: UsedFunctions, + ): string { + const asyncKeyword = needsAsync ? "async " : ""; + + // Build parameter list based on what's actually used + const params = ["config"]; + if (usedFunctions.needsGetBlock) { + params.push("getBlock"); + } + if (usedFunctions.needsGetModel) { + params.push("getModel"); + } + + const paramString = `{ ${params.join(", ")} }`; + return `export default ${asyncKeyword}function ${functionName}(${paramString}: BuilderContext)`; + } + + private createDocumentation(itemType: ItemType, type: string): string { + const escapedName = itemType.name + .replace(/'/g, "\\''") + .replace(/"/g, '\\"'); + return `/** + * Build a "${escapedName}" ${type} in DatoCMS. + * Generated from DatoCMS API on ${new Date().toISOString()} + * API Key: ${itemType.api_key.replace(/'/g, "\\''").replace(/"/g, '\\"')} + */`; + } + + private createFunctionBody( + builderClass: string, + builderConfig: string, + fieldMethods: string, + ): string { + return `return new ${builderClass}(${builderConfig})${fieldMethods};`; + } +} diff --git a/src/FileGeneration/FileGenerators/ImportGenerator.test.ts b/src/FileGeneration/FileGenerators/ImportGenerator.test.ts new file mode 100644 index 0000000..dbda711 --- /dev/null +++ b/src/FileGeneration/FileGenerators/ImportGenerator.test.ts @@ -0,0 +1,248 @@ +import { beforeEach, describe, expect, it } from "@jest/globals"; +import { ImportGenerator } from "@/FileGeneration/FileGenerators/ImportGenerator"; + +describe("ImportGenerator", () => { + let localGenerator: ImportGenerator; + let packageGenerator: ImportGenerator; + + beforeEach(() => { + localGenerator = new ImportGenerator(true); + packageGenerator = new ImportGenerator(false); + }); + + describe("generateImports - Local Development", () => { + it("should generate correct imports for BlockBuilder", () => { + const result = localGenerator.generateImports("BlockBuilder"); + + expect(result).toBe( + `import BlockBuilder from "@/BlockBuilder";\nimport type { BuilderContext } from "@/types/BuilderContext";`, + ); + }); + + it("should generate correct imports for ModelBuilder", () => { + const result = localGenerator.generateImports("ModelBuilder"); + + expect(result).toBe( + `import ModelBuilder from "@/ModelBuilder";\nimport type { BuilderContext } from "@/types/BuilderContext";`, + ); + }); + + it("should use alias path imports", () => { + const result = localGenerator.generateImports("BlockBuilder"); + + expect(result).toContain('from "@/BlockBuilder"'); + expect(result).toContain('from "@/types/BuilderContext"'); + }); + + it("should use proper import syntax for local dev", () => { + const result = localGenerator.generateImports("BlockBuilder"); + + // Should use default import for builder + expect(result).toMatch(/^import BlockBuilder from/); + + // Should use type import for BuilderContext + expect(result).toContain("import type { BuilderContext }"); + }); + }); + + describe("generateImports - Package Usage", () => { + it("should generate correct imports for BlockBuilder", () => { + const result = packageGenerator.generateImports("BlockBuilder"); + + expect(result).toBe( + `import { BlockBuilder } from "dato-builder";\nimport type { BuilderContext } from "dato-builder";`, + ); + }); + + it("should generate correct imports for ModelBuilder", () => { + const result = packageGenerator.generateImports("ModelBuilder"); + + expect(result).toBe( + `import { ModelBuilder } from "dato-builder";\nimport type { BuilderContext } from "dato-builder";`, + ); + }); + + it("should use package imports", () => { + const result = packageGenerator.generateImports("BlockBuilder"); + + expect(result).toContain('from "dato-builder"'); + expect(result).toContain( + 'import type { BuilderContext } from "dato-builder"', + ); + }); + + it("should use proper import syntax for package", () => { + const result = packageGenerator.generateImports("BlockBuilder"); + + // Should use named import for builder + expect(result).toMatch(/^import \{ BlockBuilder \} from/); + + // Should use type import for BuilderContext + expect(result).toContain("import type { BuilderContext }"); + }); + }); + + describe("shared behavior", () => { + it("should handle custom builder class names in local dev", () => { + const result = localGenerator.generateImports("CustomBuilder"); + + expect(result).toBe( + `import CustomBuilder from "@/CustomBuilder";\nimport type { BuilderContext } from "@/types/BuilderContext";`, + ); + }); + + it("should handle custom builder class names in package", () => { + const result = packageGenerator.generateImports("CustomBuilder"); + + expect(result).toBe( + `import { CustomBuilder } from "dato-builder";\nimport type { BuilderContext } from "dato-builder";`, + ); + }); + + it("should maintain consistent import structure", () => { + const blockResult = localGenerator.generateImports("BlockBuilder"); + const modelResult = localGenerator.generateImports("ModelBuilder"); + + // Both should have the same structure, just different builder class + expect(blockResult.split("\n")).toHaveLength(2); + expect(modelResult.split("\n")).toHaveLength(2); + + expect(blockResult).toContain("import type { BuilderContext }"); + expect(modelResult).toContain("import type { BuilderContext }"); + }); + + it("should handle single character builder names", () => { + const localResult = localGenerator.generateImports("B"); + const packageResult = packageGenerator.generateImports("B"); + + expect(localResult).toBe( + `import B from "@/B";\nimport type { BuilderContext } from "@/types/BuilderContext";`, + ); + expect(packageResult).toBe( + `import { B } from "dato-builder";\nimport type { BuilderContext } from "dato-builder";`, + ); + }); + + it("should handle very long builder names", () => { + const longBuilderName = + "VeryLongCustomBuilderNameThatExceedsNormalLength"; + const localResult = localGenerator.generateImports(longBuilderName); + const packageResult = packageGenerator.generateImports(longBuilderName); + + expect(localResult).toBe( + `import ${longBuilderName} from "@/${longBuilderName}";\nimport type { BuilderContext } from "@/types/BuilderContext";`, + ); + expect(packageResult).toBe( + `import { ${longBuilderName} } from "dato-builder";\nimport type { BuilderContext } from "dato-builder";`, + ); + }); + + it("should produce consistent output for same input", () => { + const result1 = localGenerator.generateImports("BlockBuilder"); + const result2 = localGenerator.generateImports("BlockBuilder"); + + expect(result1).toBe(result2); + }); + + it("should not include extra whitespace", () => { + const result = localGenerator.generateImports("BlockBuilder"); + + // Should not have trailing or leading whitespace + expect(result).not.toMatch(/^\s/); + expect(result).not.toMatch(/\s$/); + + // Should have exactly one newline between imports + expect(result.split("\n")).toHaveLength(2); + }); + + it("should use double quotes consistently", () => { + const localResult = localGenerator.generateImports("BlockBuilder"); + const packageResult = packageGenerator.generateImports("BlockBuilder"); + + expect(localResult).toContain('"@/BlockBuilder"'); + expect(localResult).toContain('"@/types/BuilderContext"'); + expect(localResult).not.toContain("'"); + + expect(packageResult).toContain('"dato-builder"'); + expect(packageResult).not.toContain("'"); + }); + }); + + describe("edge cases", () => { + it("should handle empty string builder name", () => { + const localResult = localGenerator.generateImports(""); + const packageResult = packageGenerator.generateImports(""); + + expect(localResult).toBe( + `import from "@/";\nimport type { BuilderContext } from "@/types/BuilderContext";`, + ); + expect(packageResult).toBe( + `import { } from "dato-builder";\nimport type { BuilderContext } from "dato-builder";`, + ); + }); + + it("should throw for null-like inputs", () => { + expect(() => localGenerator.generateImports(null as any)).toThrow( + "Invalid input: builderClass cannot be null or undefined", + ); + expect(() => localGenerator.generateImports(undefined as any)).toThrow( + "Invalid input: builderClass cannot be null or undefined", + ); + expect(() => packageGenerator.generateImports(null as any)).toThrow( + "Invalid input: builderClass cannot be null or undefined", + ); + expect(() => packageGenerator.generateImports(undefined as any)).toThrow( + "Invalid input: builderClass cannot be null or undefined", + ); + }); + + it("should handle numeric inputs", () => { + const localResult = localGenerator.generateImports(123 as any); + const packageResult = packageGenerator.generateImports(123 as any); + + expect(localResult).toBe( + `import 123 from "@/123";\nimport type { BuilderContext } from "@/types/BuilderContext";`, + ); + expect(packageResult).toBe( + `import { 123 } from "dato-builder";\nimport type { BuilderContext } from "dato-builder";`, + ); + }); + }); + + describe("real-world usage scenarios", () => { + it("should work correctly in typical block generation for local dev", () => { + const result = localGenerator.generateImports("BlockBuilder"); + + // The generated imports should be valid TypeScript + expect(result).toMatch(/^import \w+ from "@\//); + expect(result).toContain("import type { BuilderContext }"); + }); + + it("should work correctly in typical block generation for package", () => { + const result = packageGenerator.generateImports("BlockBuilder"); + + // The generated imports should be valid TypeScript + expect(result).toMatch(/^import \{ \w+ \} from "dato-builder"/); + expect(result).toContain("import type { BuilderContext }"); + }); + + it("should generate imports that can be combined with other code", () => { + const localImports = localGenerator.generateImports("BlockBuilder"); + const packageImports = packageGenerator.generateImports("BlockBuilder"); + const additionalCode = + "\n\nexport default function build() { return new BlockBuilder(); }"; + + const localCombined = localImports + additionalCode; + const packageCombined = packageImports + additionalCode; + + // Should be properly formatted for combining + expect(localCombined).toContain("import BlockBuilder"); + expect(localCombined).toContain("import type { BuilderContext }"); + expect(localCombined).toContain("export default function"); + + expect(packageCombined).toContain("import { BlockBuilder }"); + expect(packageCombined).toContain("import type { BuilderContext }"); + expect(packageCombined).toContain("export default function"); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerators/ImportGenerator.ts b/src/FileGeneration/FileGenerators/ImportGenerator.ts new file mode 100644 index 0000000..7177a51 --- /dev/null +++ b/src/FileGeneration/FileGenerators/ImportGenerator.ts @@ -0,0 +1,34 @@ +export class ImportGenerator { + private readonly isLocalDevelopment: boolean; + + constructor(isLocalDevelopment = false) { + this.isLocalDevelopment = isLocalDevelopment; + } + + public generateImports(builderClass: string): string { + if (builderClass === null || builderClass === undefined) { + throw new Error( + "Invalid input: builderClass cannot be null or undefined", + ); + } + + const builderImport = this.createBuilderImport(builderClass); + const contextImport = this.createBuilderContextImport(); + return `${builderImport}; +${contextImport}`; + } + + private createBuilderImport(builderClass: string): string { + if (this.isLocalDevelopment) { + return `import ${builderClass} from "@/${builderClass}"`; + } + return `import { ${builderClass} } from "dato-builder"`; + } + + private createBuilderContextImport(): string { + if (this.isLocalDevelopment) { + return 'import type { BuilderContext } from "@/types/BuilderContext";'; + } + return 'import type { BuilderContext } from "dato-builder";'; + } +} diff --git a/src/FileGeneration/FileGenerators/formatters/CodeFormatter.test.ts b/src/FileGeneration/FileGenerators/formatters/CodeFormatter.test.ts new file mode 100644 index 0000000..0fa4814 --- /dev/null +++ b/src/FileGeneration/FileGenerators/formatters/CodeFormatter.test.ts @@ -0,0 +1,432 @@ +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import type { Options as PrettierOptions } from "prettier"; +import { CodeFormatter } from "@/FileGeneration/FileGenerators/formatters/CodeFormatter"; + +// Mock prettier and eslint +jest.mock("prettier", () => ({ + format: jest.fn(), +})); + +jest.mock("eslint", () => ({ + ESLint: jest.fn(), +})); + +import { ESLint } from "eslint"; +import { format as mockPrettierFormat } from "prettier"; + +const mockFormat = mockPrettierFormat as jest.MockedFunction< + typeof mockPrettierFormat +>; +const MockedESLint = ESLint as jest.MockedClass; + +describe("CodeFormatter", () => { + let formatter: CodeFormatter; + let mockESLintInstance: jest.Mocked; + + beforeEach(() => { + jest.clearAllMocks(); + + mockESLintInstance = { + lintText: jest.fn(), + } as any; + + MockedESLint.mockImplementation(() => mockESLintInstance); + + formatter = new CodeFormatter(); + }); + + describe("constructor", () => { + it("should initialize with default prettier config", () => { + new CodeFormatter(); + + // Should create ESLint instance with correct config + expect(MockedESLint).toHaveBeenCalledWith({ + baseConfig: { + languageOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, + rules: { + "newline-per-chained-call": ["error", { ignoreChainWithDepth: 1 }], + }, + }, + fix: true, + }); + }); + + it("should accept custom prettier config", () => { + const customConfig: Partial = { + printWidth: 120, + singleQuote: true, + tabWidth: 4, + }; + + const customFormatter = new CodeFormatter(customConfig); + + expect(customFormatter).toBeInstanceOf(CodeFormatter); + }); + + it("should handle empty custom config", () => { + const emptyFormatter = new CodeFormatter({}); + + expect(emptyFormatter).toBeInstanceOf(CodeFormatter); + }); + + it("should handle undefined custom config", () => { + const undefinedFormatter = new CodeFormatter(undefined); + + expect(undefinedFormatter).toBeInstanceOf(CodeFormatter); + }); + }); + + describe("format", () => { + beforeEach(() => { + mockFormat.mockResolvedValue("formatted code"); + mockESLintInstance.lintText.mockResolvedValue([ + { + output: "eslint formatted code", + }, + ] as any); + }); + + it("should format code using prettier and eslint", async () => { + const inputCode = "const test = 'hello';"; + + const result = await formatter.format(inputCode); + + expect(mockFormat).toHaveBeenCalledWith(inputCode, { + parser: "typescript", + singleQuote: false, + trailingComma: "es5", + tabWidth: 2, + semi: true, + printWidth: 30, + bracketSpacing: true, + arrowParens: "avoid", + bracketSameLine: false, + singleAttributePerLine: false, + }); + + expect(mockESLintInstance.lintText).toHaveBeenCalledWith( + "formatted code", + ); + expect(result).toBe("eslint formatted code"); + }); + + it("should use custom prettier config when provided", async () => { + const customConfig: Partial = { + printWidth: 120, + singleQuote: true, + tabWidth: 4, + }; + + const customFormatter = new CodeFormatter(customConfig); + const inputCode = "const test = 'hello';"; + + await customFormatter.format(inputCode); + + expect(mockFormat).toHaveBeenCalledWith(inputCode, { + parser: "typescript", + singleQuote: true, // overridden + trailingComma: "es5", + tabWidth: 4, // overridden + semi: true, + printWidth: 120, // overridden + bracketSpacing: true, + arrowParens: "avoid", + bracketSameLine: false, + singleAttributePerLine: false, + }); + }); + + it("should fallback to original code when prettier fails", async () => { + const inputCode = "const test = 'hello';"; + mockFormat.mockRejectedValue(new Error("Prettier failed")); + + const result = await formatter.format(inputCode); + + expect(mockESLintInstance.lintText).toHaveBeenCalledWith(inputCode); + expect(result).toBe("eslint formatted code"); + }); + + it("should fallback to prettier output when eslint fails", async () => { + const inputCode = "const test = 'hello';"; + mockESLintInstance.lintText.mockRejectedValue(new Error("ESLint failed")); + + const result = await formatter.format(inputCode); + + expect(result).toBe("formatted code"); + }); + + it("should fallback to original code when both prettier and eslint fail", async () => { + const inputCode = "const test = 'hello';"; + mockFormat.mockRejectedValue(new Error("Prettier failed")); + mockESLintInstance.lintText.mockRejectedValue(new Error("ESLint failed")); + + const result = await formatter.format(inputCode); + + expect(result).toBe(inputCode); + }); + + it("should use eslint output when available", async () => { + const inputCode = "const test = 'hello';"; + mockESLintInstance.lintText.mockResolvedValue([ + { + output: "eslint output", + }, + ] as any); + + const result = await formatter.format(inputCode); + + expect(result).toBe("eslint output"); + }); + + it("should fallback to prettier output when eslint output is undefined", async () => { + const inputCode = "const test = 'hello';"; + mockESLintInstance.lintText.mockResolvedValue([ + { + output: undefined, + }, + ] as any); + + const result = await formatter.format(inputCode); + + expect(result).toBe("formatted code"); + }); + + it("should fallback to prettier output when eslint output is null", async () => { + const inputCode = "const test = 'hello';"; + mockESLintInstance.lintText.mockResolvedValue([ + { + output: null, + }, + ] as any); + + const result = await formatter.format(inputCode); + + expect(result).toBe("formatted code"); + }); + + it("should handle empty input code", async () => { + const result = await formatter.format(""); + + expect(mockFormat).toHaveBeenCalledWith("", expect.any(Object)); + expect(result).toBe("eslint formatted code"); + }); + + it("should handle multiline code", async () => { + const multilineCode = ` + function test() { + const x = 1; + const y = 2; + return x + y; + } + `; + + await formatter.format(multilineCode); + + expect(mockFormat).toHaveBeenCalledWith( + multilineCode, + expect.any(Object), + ); + }); + + it("should handle code with syntax errors gracefully", async () => { + const invalidCode = "const test = ;"; + mockFormat.mockRejectedValue(new Error("Syntax error")); + mockESLintInstance.lintText.mockRejectedValue(new Error("Syntax error")); + + const result = await formatter.format(invalidCode); + + expect(result).toBe(invalidCode); + }); + + it("should preserve code content through formatting", async () => { + const complexCode = ` + import BlockBuilder from "../../BlockBuilder"; + import type { BuilderContext } from "../../types/BuilderContext"; + + export default function buildTestBlock({ config }: BuilderContext) { + return new BlockBuilder({ + name: 'Test Block', + config, + options: { api_key: 'test_block' } + }).addText({ label: 'Title' }); + } + `; + + mockFormat.mockResolvedValue(complexCode.trim()); + mockESLintInstance.lintText.mockResolvedValue([ + { + output: complexCode.trim(), + }, + ] as any); + + const result = await formatter.format(complexCode); + + expect(result).toBe(complexCode.trim()); + }); + }); + + describe("prettier configuration", () => { + it("should use typescript parser by default", async () => { + await formatter.format("const test = 'hello';"); + + expect(mockFormat).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ parser: "typescript" }), + ); + }); + + it("should use specified print width", async () => { + await formatter.format("const test = 'hello';"); + + expect(mockFormat).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ printWidth: 30 }), + ); + }); + + it("should use double quotes by default", async () => { + await formatter.format("const test = 'hello';"); + + expect(mockFormat).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ singleQuote: false }), + ); + }); + + it("should use semicolons by default", async () => { + await formatter.format("const test = 'hello';"); + + expect(mockFormat).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ semi: true }), + ); + }); + + it("should allow custom config to override defaults", async () => { + const customFormatter = new CodeFormatter({ + singleQuote: true, + semi: false, + printWidth: 100, + }); + + await customFormatter.format("const test = 'hello';"); + + expect(mockFormat).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + singleQuote: true, + semi: false, + printWidth: 100, + }), + ); + }); + }); + + describe("eslint configuration", () => { + it("should configure eslint with chained call rules", () => { + new CodeFormatter(); + + expect(MockedESLint).toHaveBeenCalledWith( + expect.objectContaining({ + baseConfig: expect.objectContaining({ + rules: { + "newline-per-chained-call": [ + "error", + { ignoreChainWithDepth: 1 }, + ], + }, + }), + }), + ); + }); + + it("should enable eslint auto-fix", () => { + new CodeFormatter(); + + expect(MockedESLint).toHaveBeenCalledWith( + expect.objectContaining({ + fix: true, + }), + ); + }); + + it("should configure for ES2020 modules", () => { + new CodeFormatter(); + + expect(MockedESLint).toHaveBeenCalledWith( + expect.objectContaining({ + baseConfig: expect.objectContaining({ + languageOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, + }), + }), + ); + }); + }); + + describe("error handling edge cases", () => { + it("should handle prettier throwing non-Error objects", async () => { + const inputCode = "const test = 'hello';"; + mockFormat.mockRejectedValue("String error"); + mockESLintInstance.lintText.mockRejectedValue(new Error("ESLint failed")); + + const result = await formatter.format(inputCode); + + expect(result).toBe(inputCode); + }); + + it("should handle eslint throwing non-Error objects", async () => { + const inputCode = "const test = 'hello';"; + mockFormat.mockResolvedValue("formatted code"); + mockESLintInstance.lintText.mockRejectedValue("String error"); + + const result = await formatter.format(inputCode); + + expect(result).toBe("formatted code"); + }); + + it("should handle eslint returning empty results array", async () => { + const inputCode = "const test = 'hello';"; + mockFormat.mockResolvedValue("formatted code"); + mockESLintInstance.lintText.mockResolvedValue([]); + + const result = await formatter.format(inputCode); + + expect(result).toBe("formatted code"); + }); + + it("should handle eslint returning multiple results", async () => { + const inputCode = "const test = 'hello';"; + mockESLintInstance.lintText.mockResolvedValue([ + { output: "first result" }, + { output: "second result" }, + ] as any); + + const result = await formatter.format(inputCode); + + // Should use the first result + expect(result).toBe("first result"); + }); + + it("should handle very large code inputs", async () => { + const largeCode = "const test = 'hello';\n".repeat(10000); + + await formatter.format(largeCode); + + expect(mockFormat).toHaveBeenCalledWith(largeCode, expect.any(Object)); + }); + + it("should handle code with special characters", async () => { + const specialCode = "const test = 'πŸš€ Hello δΈ–η•Œ \u0000 \u200B';"; + + await formatter.format(specialCode); + + expect(mockFormat).toHaveBeenCalledWith(specialCode, expect.any(Object)); + }); + }); +}); diff --git a/src/FileGeneration/FileGenerators/formatters/CodeFormatter.ts b/src/FileGeneration/FileGenerators/formatters/CodeFormatter.ts new file mode 100644 index 0000000..44fd37c --- /dev/null +++ b/src/FileGeneration/FileGenerators/formatters/CodeFormatter.ts @@ -0,0 +1,51 @@ +import { ESLint } from "eslint"; +import { format, type Options as PrettierOptions } from "prettier"; +export class CodeFormatter { + private readonly prettierConfig: PrettierOptions = { + parser: "typescript", + singleQuote: false, + trailingComma: "es5", + tabWidth: 2, + semi: true, + printWidth: 30, + bracketSpacing: true, + arrowParens: "avoid", + bracketSameLine: false, + singleAttributePerLine: false, + }; + + private readonly eslint = new ESLint({ + baseConfig: { + languageOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, + rules: { + // break chains longer than depth 1: + "newline-per-chained-call": ["error", { ignoreChainWithDepth: 1 }], + }, + }, + fix: true, + }); + + constructor(private readonly config: Partial = {}) {} + + async format(code: string): Promise { + let formatted = code; + try { + formatted = await format(code, { + ...this.prettierConfig, + ...this.config, + }); + } catch (_e) { + // fallback to original + } + + try { + const [result] = await this.eslint.lintText(formatted); + return result?.output ?? formatted; + } catch (_e) { + return formatted; + } + } +} diff --git a/tests/builder.test.ts b/src/ItemTypeBuilder.test.ts similarity index 83% rename from tests/builder.test.ts rename to src/ItemTypeBuilder.test.ts index 789a97a..a1c0686 100644 --- a/tests/builder.test.ts +++ b/src/ItemTypeBuilder.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { createMockConfig } from "@tests/utils/mockConfig"; +import type { DatoBuilderConfig } from "../src"; import DatoApi from "../src/Api/DatoApi"; -import NotFoundError from "../src/Api/Error/NotFoundError"; -import * as configLoader from "../src/config/loader"; import Field, { type FieldBody } from "../src/Fields/Field"; import Integer from "../src/Fields/Integer"; import Markdown from "../src/Fields/Markdown"; @@ -21,20 +21,16 @@ jest.mock("../src/Fields/Textarea"); jest.mock("../src/Fields/StringRadioGroup"); jest.mock("../src/Fields/StringSelect"); jest.mock("../src/Fields/MultiLineText"); -jest.mock("../src/config/loader", () => ({ - loadDatoBuilderConfig: jest.fn(() => ({ - overwriteExistingFields: false, - })), -})); -jest.mock("../src/config", () => ({ - getDatoClient: jest.fn(() => ({})), -})); // Same TestItemTypeBuilder class as in the original file class TestItemTypeBuilder extends ItemTypeBuilder { // biome-ignore lint/suspicious/noExplicitAny: It's a test - constructor(type: ItemTypeBuilderType, body: any, config: any = {}) { - super(type, body, config); + constructor( + type: ItemTypeBuilderType, + body: any, + overwriteConfig?: Partial, + ) { + super({ type, body, config: createMockConfig(overwriteConfig) }); } } @@ -47,9 +43,6 @@ describe("ItemTypeBuilder", () => { let mockClientFields: any; beforeEach(() => { - // Wipe both the in-memory and on-disk caches - ItemTypeBuilder.clearCache(); - // Clear all mocks jest.clearAllMocks(); @@ -58,6 +51,7 @@ describe("ItemTypeBuilder", () => { create: jest.fn(async (_body) => ({ id: "item-123" })), update: jest.fn(async () => ({ id: "item-123" })), find: jest.fn(async () => ({ id: "item-123" })), + list: jest.fn(async () => []), }; mockClientFields = { @@ -104,7 +98,7 @@ describe("ItemTypeBuilder", () => { sortable: true, draft_mode_active: false, all_locales_required: true, - api_key: "test_model", + api_key: "test_model_custom_model", modular_block: false, }); }); @@ -132,70 +126,22 @@ describe("ItemTypeBuilder", () => { }); expect(blockBuilder.body.modular_block).toBe(true); - expect(blockBuilder.body.api_key).toBe("test_block"); + expect(blockBuilder.body.api_key).toBe("test_block_custom_block"); }); + }); - it("should merge configs correctly", () => { - // Test default config - expect(builder.config).toEqual({ - overwriteExistingFields: false, - debug: false, - blockApiKeySuffix: "", - modelApiKeySuffix: "", - }); - - // Test with global config override - (configLoader.loadDatoBuilderConfig as jest.Mock).mockReturnValueOnce({ - overwriteExistingFields: true, - debug: true, - blockApiKeySuffix: "block_suffix", - modelApiKeySuffix: "model_suffix", - }); - - const globalConfigBuilder = new TestItemTypeBuilder("model", { - name: "Global Config Model", - singleton: false, - sortable: true, - draft_mode_active: false, - all_locales_required: true, - }); - - expect(globalConfigBuilder.config).toEqual({ - overwriteExistingFields: true, - debug: true, - blockApiKeySuffix: "block_suffix", - modelApiKeySuffix: "model_suffix", - }); - - // Test with builder-specific config - const specificConfigBuilder = new TestItemTypeBuilder( + describe("setOverrideExistingFields", () => { + it("should update the config and return this", () => { + const builder = new TestItemTypeBuilder( "model", { - name: "Specific Config Model", - singleton: false, - sortable: true, - draft_mode_active: false, - all_locales_required: true, + name: "Test Model", }, { - overwriteExistingFields: true, - debug: false, - blockApiKeySuffix: "specific_block_suffix", - modelApiKeySuffix: "specific_model_suffix", + overwriteExistingFields: false, }, ); - expect(specificConfigBuilder.config).toEqual({ - overwriteExistingFields: true, - debug: false, - blockApiKeySuffix: "specific_block_suffix", - modelApiKeySuffix: "specific_model_suffix", - }); - }); - }); - - describe("setOverrideExistingFields", () => { - it("should update the config and return this", () => { expect(builder.config.overwriteExistingFields).toBe(false); const result = builder.setOverrideExistingFields(true); @@ -216,9 +162,15 @@ describe("ItemTypeBuilder", () => { describe("api key suffix", () => { describe("item type block", () => { it("does not add a suffix to the api_key", () => { - const blockBuilder = new TestItemTypeBuilder("block", { - name: "Test", - }); + const blockBuilder = new TestItemTypeBuilder( + "block", + { + name: "Test", + }, + { + blockApiKeySuffix: null, + }, + ); expect(blockBuilder.body.api_key).toBe("test"); }); @@ -240,9 +192,15 @@ describe("ItemTypeBuilder", () => { describe("item type model", () => { it("does not add a suffix to the api_key", () => { - const modelBuilder = new TestItemTypeBuilder("model", { - name: "Test", - }); + const modelBuilder = new TestItemTypeBuilder( + "model", + { + name: "Test", + }, + { + modelApiKeySuffix: null, + }, + ); expect(modelBuilder.body.api_key).toBe("test"); }); @@ -265,9 +223,15 @@ describe("ItemTypeBuilder", () => { describe("plural api key", () => { it("should return the singular api_key of the item when the item name is plural", () => { - const pluralBuilder = new TestItemTypeBuilder("model", { - name: "Test Models", - }); + const pluralBuilder = new TestItemTypeBuilder( + "model", + { + name: "Test Models", + }, + { + modelApiKeySuffix: null, + }, + ); expect(pluralBuilder.body.api_key).toBe("test_model"); }); @@ -335,7 +299,7 @@ describe("ItemTypeBuilder", () => { }), ); - expect(builder.fields[0].build().api_key).toBe("fields"); + expect(builder.fields[0]?.build().api_key).toBe("fields"); }); }); @@ -416,13 +380,21 @@ describe("ItemTypeBuilder", () => { builder.addField(mockField); + mockClientItemTypes.list.mockResolvedValueOnce([ + { + id: "item-123", + api_key: "test_model_custom_model", + name: "Test Model", + }, + ]); + const result = await builder.update(); - expect(mockApiCall).toHaveBeenCalledTimes(3); - expect(mockClientItemTypes.update).toHaveBeenCalledWith( - "test_model", - builder.body, - ); + expect(mockApiCall).toHaveBeenCalledTimes(4); + expect(mockClientItemTypes.update).toHaveBeenCalledWith("item-123", { + ...builder.body, + hint: null, + }); expect(mockClientFields.list).toHaveBeenCalledWith("item-123"); expect(mockClientFields.create).toHaveBeenCalledWith("item-123", { api_key: "test_field", @@ -440,6 +412,14 @@ describe("ItemTypeBuilder", () => { .mockReturnValue({ api_key: "test_field", label: "Test Field" }), } as unknown as Field; + mockClientItemTypes.list.mockResolvedValueOnce([ + { + id: "item-123", + api_key: "test_model_custom_model", + name: "Test Model", + }, + ]); + builder.addField(mockField); // Mock an existing field @@ -460,39 +440,31 @@ describe("ItemTypeBuilder", () => { describe("upsert", () => { it("should update if the item type exists", async () => { - const updateSpy = jest - .spyOn(builder, "update") - .mockResolvedValue("item-123"); - const createSpy = jest.spyOn(builder, "create"); + const updateSpy = jest.spyOn(mockClientItemTypes, "update"); + const createSpy = jest.spyOn(mockClientItemTypes, "create"); + + mockClientItemTypes.list.mockResolvedValueOnce([ + { id: "item-123", name: "Test Model" }, + ]); const result = await builder.upsert(); - expect(mockClientItemTypes.find).toHaveBeenCalledWith("test_model"); + expect(mockClientItemTypes.list).toHaveBeenCalled(); expect(updateSpy).toHaveBeenCalled(); expect(createSpy).not.toHaveBeenCalled(); expect(result).toBe("item-123"); }); it("should create if the item type does not exist", async () => { - const updateSpy = jest.spyOn(builder, "update"); - const createSpy = jest - .spyOn(builder, "create") - .mockResolvedValue("item-456"); - - // Mock not found error - mockClientItemTypes.find.mockRejectedValueOnce( - new NotFoundError({ - outerCode: "NOT_FOUND", - innerCode: "ITEM_TYPE_NOT_FOUND", - details: {}, - docUrl: "", - transient: false, - }), - ); + const updateSpy = jest.spyOn(mockClientItemTypes, "update"); + const createSpy = jest.spyOn(mockClientItemTypes, "create"); + + mockClientItemTypes.list.mockResolvedValueOnce([]); + mockClientItemTypes.create.mockResolvedValueOnce({ id: "item-456" }); const result = await builder.upsert(); - expect(mockClientItemTypes.find).toHaveBeenCalledWith("test_model"); + expect(mockClientItemTypes.list).toHaveBeenCalled(); expect(updateSpy).not.toHaveBeenCalled(); expect(createSpy).toHaveBeenCalled(); expect(result).toBe("item-456"); @@ -500,7 +472,7 @@ describe("ItemTypeBuilder", () => { it("should propagate other errors", async () => { const genericError = new Error("Generic error"); - mockClientItemTypes.find.mockRejectedValueOnce(genericError); + mockClientItemTypes.list.mockRejectedValueOnce(genericError); await expect(builder.upsert()).rejects.toThrow(genericError); }); @@ -693,28 +665,70 @@ describe("ItemTypeBuilder", () => { }); }); - describe("syncFields", () => { - it("should create new fields that don't exist yet", async () => { + describe("Error handling", () => { + it("should propagate API errors when creating fields", async () => { const mockField = { - build: jest.fn().mockReturnValue({ - api_key: "new_field", - label: "New Field", - }), + build: jest.fn().mockReturnValue({ api_key: "test_field" }), } as unknown as Field; builder.addField(mockField); - // Execute the private syncFields method via the public create method - await builder.create(); + const apiError = new Error("API Error"); + mockClientFields.create.mockRejectedValueOnce(apiError); - expect(mockClientFields.list).toHaveBeenCalledWith("item-123"); - expect(mockClientFields.create).toHaveBeenCalledWith("item-123", { - api_key: "new_field", - label: "New Field", - }); - // No updates or deletes should happen - expect(mockClientFields.update).not.toHaveBeenCalled(); - expect(mockClientFields.destroy).not.toHaveBeenCalled(); + await expect(builder.create()).rejects.toThrow( + 'Failed to create field "test_field": API Error', + ); + }); + + it("should propagate API errors when updating fields", async () => { + const mockField = { + build: jest.fn().mockReturnValue({ api_key: "test_field" }), + } as unknown as Field; + + builder.addField(mockField); + builder.setOverrideExistingFields(true); + + mockClientItemTypes.list.mockResolvedValueOnce([ + { id: "item-123", name: "Test Model" }, + ]); + mockClientFields.list.mockResolvedValueOnce([ + { id: "field-123", api_key: "test_field" }, + ]); + + const apiError = new Error("API Error"); + mockClientFields.update.mockRejectedValueOnce(apiError); + + await expect(builder.update()).rejects.toThrow( + 'Failed to update field "test_field" (id: field-123): API Error', + ); + }); + + it("should propagate API errors when deleting fields", async () => { + builder.setOverrideExistingFields(true); + + mockClientItemTypes.list.mockResolvedValueOnce([ + { id: "item-123", name: "Test Model" }, + ]); + mockClientFields.list.mockResolvedValueOnce([ + { id: "field-123", api_key: "to_delete" }, + ]); + + const apiError = new Error("API Error"); + mockClientFields.destroy.mockRejectedValueOnce(apiError); + + await expect(builder.update()).rejects.toThrow( + 'Failed to delete field "to_delete" (id: field-123): API Error', + ); + }); + }); + + describe("syncFields", () => { + beforeEach(() => { + mockClientItemTypes.list.mockResolvedValue([ + { id: "item-123", name: "Test Model" }, + ]); + mockClientFields.list.mockResolvedValue([]); }); it("should update existing fields when overwriteExistingFields is true", async () => { @@ -740,6 +754,8 @@ describe("ItemTypeBuilder", () => { api_key: "existing_field", label: "Updated Field", }); + expect(mockClientFields.create).not.toHaveBeenCalled(); + expect(mockClientFields.destroy).not.toHaveBeenCalled(); }); it("should delete fields no longer in the builder when overwriteExistingFields is true", async () => { @@ -765,6 +781,11 @@ describe("ItemTypeBuilder", () => { // Should delete field2 since it's not in the builder expect(mockClientFields.destroy).toHaveBeenCalledWith("field-456"); + expect(mockClientFields.create).not.toHaveBeenCalled(); + expect(mockClientFields.update).toHaveBeenCalledWith("field-123", { + api_key: "field1", + label: "Field 1", + }); }); it("should not update or delete fields when overwriteExistingFields is false", async () => { @@ -792,6 +813,33 @@ describe("ItemTypeBuilder", () => { // No updates or deletes should happen expect(mockClientFields.update).not.toHaveBeenCalled(); expect(mockClientFields.destroy).not.toHaveBeenCalled(); + expect(mockClientFields.create).not.toHaveBeenCalled(); + }); + + it("should create new fields that do not exist", async () => { + // Mock existing fields + mockClientFields.list.mockResolvedValueOnce([ + { id: "field-123", api_key: "field1", label: "Field 1" }, + ]); + + // Add a new field + const mockField = { + build: jest.fn().mockReturnValue({ + api_key: "new_field", + label: "New Field", + }), + } as unknown as Field; + + builder.addField(mockField); + + await builder.update(); + + expect(mockClientFields.create).toHaveBeenCalledWith("item-123", { + api_key: "new_field", + label: "New Field", + }); + expect(mockClientFields.update).not.toHaveBeenCalled(); + expect(mockClientFields.destroy).not.toHaveBeenCalled(); }); }); @@ -855,21 +903,6 @@ describe("ItemTypeBuilder", () => { }); }); - describe("Error handling", () => { - it("should propagate API errors when creating fields", async () => { - const mockField = { - build: jest.fn().mockReturnValue({ api_key: "test_field" }), - } as unknown as Field; - - builder.addField(mockField); - - const apiError = new Error("API Error"); - mockClientFields.create.mockRejectedValueOnce(apiError); - - await expect(builder.create()).rejects.toThrow(apiError); - }); - }); - describe("Integration tests", () => { it("should handle a complete model with multiple field types", async () => { // Create mock fields of different types diff --git a/src/ItemTypeBuilder.ts b/src/ItemTypeBuilder.ts index dc6f3df..bc02606 100644 --- a/src/ItemTypeBuilder.ts +++ b/src/ItemTypeBuilder.ts @@ -1,13 +1,7 @@ import { createHash } from "node:crypto"; -import fs from "node:fs"; -import path from "node:path"; import type * as SimpleSchemaTypes from "@datocms/cma-client/src/generated/SimpleSchemaTypes"; +import { buildClient } from "@datocms/cma-client-node"; import DatoApi from "./Api/DatoApi"; -import GenericDatoError from "./Api/Error/GenericDatoError"; -import NotFoundError from "./Api/Error/NotFoundError"; -import UniquenessError from "./Api/Error/UniquenessError"; -import { getDatoClient } from "./config"; -import { loadDatoBuilderConfig } from "./config/loader"; import AssetGallery, { type AssetGalleryConfig } from "./Fields/AssetGallery"; import BooleanField, { type BooleanConfig } from "./Fields/Boolean"; import BooleanRadioGroup, { @@ -57,6 +51,8 @@ import StructuredText, { import Textarea, { type TextareaConfig } from "./Fields/Textarea"; import Url, { type UrlConfig } from "./Fields/Url"; import Wysiwyg, { type WysiwygConfig } from "./Fields/Wysiwyg"; +import { ConsoleLogger } from "./logger"; +import type { DatoBuilderConfig } from "./types/DatoBuilderConfig"; import { executeWithErrorHandling } from "./utils/errors"; import { generateDatoApiKey } from "./utils/utils"; @@ -69,68 +65,52 @@ export type ItemTypeBuilderBody = Omit< api_key?: string; }; -export type ItemTypeBuilderConfig = { - /** - * Whether to overwrite existing fields in DatoCMS when syncing. - * - * - `false` (default): New fields will be created. All other fields - * will be left untouched. - * - * - `true`: Fields with matching API keys will be updated to match - * your code definitions, overwriting any manual changes made via - * the DatoCMS dashboard. - */ - overwriteExistingFields?: boolean; - debug?: boolean; - /** Suffix to append to model API keys */ - modelApiKeySuffix?: string; - /** Suffix to append to block API keys */ - blockApiKeySuffix?: string; +type ItemTypeBuilderOptions = { + type: ItemTypeBuilderType; + body: Omit & { + api_key?: string; + }; + config: Required; }; -// For tracking in-progress operations -interface PendingOperation { - promise: Promise; - timestamp: number; -} +type HashableConfigKeys = "modelApiKeySuffix" | "blockApiKeySuffix"; + +type HashableConfig = Pick; export default abstract class ItemTypeBuilder { - protected api = new DatoApi(getDatoClient()); - protected readonly client = this.api.client; + protected logger: ConsoleLogger; + protected api: DatoApi; readonly body: SimpleSchemaTypes.ItemTypeCreateSchema; readonly name: string; readonly type: ItemTypeBuilderType; readonly fields: Field[] = []; - readonly config: Required; - - // Persistent cache file for item definitions - private static cacheFile = path.resolve( - __dirname, - ".itemTypeBuilderCache.json", - ); - private static cacheLoaded = false; - private static itemCache: Map = - new Map(); - - private static pendingOperations: Map = new Map(); - private static lockFile = path.resolve( - __dirname, - ".itemTypeBuilderCache.lock", - ); - private static readonly PENDING_OPERATION_TIMEOUT = 60000; // 60 seconds - - protected constructor( - type: ItemTypeBuilderType, - body: Omit & { - api_key?: string; - }, - config: ItemTypeBuilderConfig = {}, - ) { + readonly config: Required; + + protected constructor({ type, body, config }: ItemTypeBuilderOptions) { + this.logger = new ConsoleLogger(config.logLevel); + this.logger.traceJson("Initializing ItemTypeBuilder", { + type, + name: body.name, + config: { + logLevel: config.logLevel, + apiToken: config.apiToken ? "***" : "undefined", + overwriteExistingFields: config.overwriteExistingFields, + modelApiKeySuffix: config.modelApiKeySuffix, + blockApiKeySuffix: config.blockApiKeySuffix, + }, + }); + this.type = type; this.name = body.name; + this.api = new DatoApi(buildClient({ apiToken: config.apiToken }), this.logger); + + this.config = config; - // Merge builder-specific and global config - this.config = this.mergeConfig(config); + this.logger.traceJson("Generating API key", { + originalKey: body.api_key, + name: body.name, + suffix: this.resolveSuffix(), + }); const apiKey = body.api_key || @@ -140,6 +120,12 @@ export default abstract class ItemTypeBuilder { preservePlural: false, }); + this.logger.traceJson("API key generated", { + originalKey: body.api_key, + generatedKey: apiKey, + suffix: this.resolveSuffix(), + }); + this.body = { ...body, api_key: apiKey, @@ -147,255 +133,138 @@ export default abstract class ItemTypeBuilder { collection_appearance: body.collection_appearance || "table", }; - if (this.config.debug) { - console.info( - `ItemTypeBuilder initialized with type "${this.type}" and API key "${apiKey}"`, - ); - - console.info( - `ItemTypeBuilder body: ${JSON.stringify(this.body, null, 2)}`, - ); - - console.info( - `ItemTypeBuilder config: ${JSON.stringify(this.config, null, 2)}`, - ); - } - } - - /** - * Attempt to acquire a lock for cache operations - * Uses a simple file-based lock with exponential backoff - */ - private static async acquireLock(maxAttempts = 10): Promise { - for (let attempt = 0; attempt < maxAttempts; attempt++) { - try { - if (!fs.existsSync(ItemTypeBuilder.lockFile)) { - // Create the lock file - fs.writeFileSync( - ItemTypeBuilder.lockFile, - String(Date.now()), - "utf8", - ); - return true; - } - - // Check if the lock is stale (more than 30 seconds old) - const lockTime = Number.parseInt( - fs.readFileSync(ItemTypeBuilder.lockFile, "utf8"), - ); - if (Date.now() - lockTime > 30000) { - // Lock is stale, override it - fs.writeFileSync( - ItemTypeBuilder.lockFile, - String(Date.now()), - "utf8", - ); - return true; - } - - // Wait with exponential backoff - const waitTime = Math.min(100 * 2 ** attempt, 2000); - await new Promise((resolve) => setTimeout(resolve, waitTime)); - } catch (err) { - console.warn(`Failed to acquire lock on attempt ${attempt + 1}:`, err); - } - } - - return false; - } - - /** - * Release the lock for cache operations - */ - private static releaseLock(): void { - try { - if (fs.existsSync(ItemTypeBuilder.lockFile)) { - fs.unlinkSync(ItemTypeBuilder.lockFile); - } - } catch (err) { - console.warn("Failed to release lock:", err); - } - } - - /** - * Clean up stale pending operations - */ - private static cleanPendingOperations(): void { - const now = Date.now(); - ItemTypeBuilder.pendingOperations.forEach((operation, key) => { - if ( - now - operation.timestamp > - ItemTypeBuilder.PENDING_OPERATION_TIMEOUT - ) { - ItemTypeBuilder.pendingOperations.delete(key); - } + this.logger.traceJson("ItemTypeBuilder initialized", { + type: this.type, + name: this.name, + apiKey: this.body.api_key, + modularBlock: this.body.modular_block, + collectionAppearance: this.body.collection_appearance, }); } - private static async loadCache(): Promise { - if (ItemTypeBuilder.cacheLoaded) return; + private getHashableConfig(): HashableConfig { + this.logger.traceJson("Getting hashable config", {}); - try { - await ItemTypeBuilder.acquireLock(); - - if (fs.existsSync(ItemTypeBuilder.cacheFile)) { - try { - const data = fs.readFileSync(ItemTypeBuilder.cacheFile, "utf8"); - const obj = JSON.parse(data) as Record< - string, - { hash: string; id: string } - >; - ItemTypeBuilder.itemCache = new Map(Object.entries(obj)); - } catch (e) { - console.warn("Failed to load itemTypeBuilder cache:", e); - } - } - ItemTypeBuilder.cacheLoaded = true; - } finally { - ItemTypeBuilder.releaseLock(); - } - } + const config: HashableConfig = { + modelApiKeySuffix: this.config.modelApiKeySuffix, + blockApiKeySuffix: this.config.blockApiKeySuffix, + }; - public static clearCache(): void { - // Clear in-memory cache - ItemTypeBuilder.itemCache.clear(); - ItemTypeBuilder.cacheLoaded = false; - - if (fs.existsSync(ItemTypeBuilder.cacheFile)) { - try { - fs.unlinkSync(ItemTypeBuilder.cacheFile); - console.log("βœ… ItemTypeBuilder cache file removed."); - } catch (e) { - console.warn("⚠️ Failed to delete cache file:", e); - } - } else { - console.log("⚠️ No cache file found, nothing to clear."); - } + this.logger.traceJson("Hashable config retrieved", config); + return config; } - private static async saveCache(): Promise { - try { - const locked = await ItemTypeBuilder.acquireLock(); - if (!locked) { - console.warn( - "Failed to acquire lock for saving cache, will try again later", - ); - return; - } - - const obj: Record = {}; - ItemTypeBuilder.itemCache.forEach((val, key) => { - obj[key] = val; - }); + public getHash(): string { + this.logger.traceJson("Generating hash for ItemTypeBuilder", { + type: this.type, + name: this.name, + fieldCount: this.fields.length, + }); - try { - fs.writeFileSync( - ItemTypeBuilder.cacheFile, - JSON.stringify(obj, null, 2), - "utf8", - ); - } catch (e) { - console.warn("Failed to save itemTypeBuilder cache:", e); - } - } finally { - ItemTypeBuilder.releaseLock(); - } - } + const hashData = { + body: this.body, + fields: [...this.fields.map((f) => f.build())].sort((a, b) => + a.api_key.localeCompare(b.api_key), + ), + config: this.getHashableConfig(), + }; - private static computeHash( - body: SimpleSchemaTypes.ItemTypeCreateSchema, - fields: SimpleSchemaTypes.FieldCreateSchema[], - config: ItemTypeBuilderConfig = {}, - ): string { - const sorted = [...fields].sort((a, b) => - a.api_key.localeCompare(b.api_key), - ); - const serialized = JSON.stringify({ body, fields: sorted, config }); - return createHash("sha256").update(serialized).digest("hex"); - } + this.logger.traceJson("Hash data prepared", { + bodyKeys: Object.keys(hashData.body), + fieldCount: hashData.fields.length, + configKeys: Object.keys(hashData.config), + }); - private static async getCache( - apiKey: string, - ): Promise<{ hash: string; id: string } | undefined> { - await ItemTypeBuilder.loadCache(); - return ItemTypeBuilder.itemCache.get(apiKey); - } + const hash = createHash("sha256") + .update(JSON.stringify(hashData)) + .digest("hex"); - private static async setCache( - apiKey: string, - hash: string, - id: string, - ): Promise { - await ItemTypeBuilder.loadCache(); - ItemTypeBuilder.itemCache.set(apiKey, { hash, id }); - await ItemTypeBuilder.saveCache(); - } + this.logger.traceJson("Hash generated", { + hash: `${hash.substring(0, 8)}...`, + fieldCount: this.fields.length, + hashDataKeys: Object.keys(hashData), + }); - private getDefinitionHash(): string { - const defs = this.fields.map((f) => f.build()); - return ItemTypeBuilder.computeHash(this.body, defs, this.config); + return hash; } - private mergeConfig( - builderConfig: ItemTypeBuilderConfig, - ): Required { - const globalConfig = loadDatoBuilderConfig(); - return { - overwriteExistingFields: - builderConfig.overwriteExistingFields ?? - globalConfig.overwriteExistingFields ?? - false, - debug: builderConfig.debug ?? globalConfig.debug ?? false, - modelApiKeySuffix: - builderConfig.modelApiKeySuffix ?? globalConfig.modelApiKeySuffix ?? "", - blockApiKeySuffix: - builderConfig.blockApiKeySuffix ?? globalConfig.blockApiKeySuffix ?? "", - }; - } + private resolveSuffix(): string | null | undefined { + this.logger.traceJson("Resolving suffix for type", { type: this.type }); - private resolveSuffix(): string | undefined { switch (this.type) { - case "model": - return this.config.modelApiKeySuffix; - case "block": - return this.config.blockApiKeySuffix; + case "model": { + const modelSuffix = this.config.modelApiKeySuffix; + this.logger.traceJson("Using model suffix", { suffix: modelSuffix }); + return modelSuffix; + } + case "block": { + const blockSuffix = this.config.blockApiKeySuffix; + this.logger.traceJson("Using block suffix", { suffix: blockSuffix }); + return blockSuffix; + } default: + this.logger.errorJson("Unknown type for suffix resolution", { + type: this.type, + }); throw new Error(`Unknown type "${this.type}"`); } } public setOverrideExistingFields(value = true): this { + this.logger.traceJson("Setting override existing fields", { value }); this.config.overwriteExistingFields = value; return this; } public addField(field: Field): this { const key = field.build().api_key; + this.logger.traceJson("Adding field", { + fieldKey: key, + fieldType: field.constructor.name, + currentFieldCount: this.fields.length, + }); + if (this.fields.some((f) => f.build().api_key === key)) { + this.logger.errorJson("Field with duplicate API key", { + fieldKey: key, + existingFields: this.fields.map((f) => f.build().api_key), + }); throw new Error(`Field with api_key "${key}" already exists.`); } - if (this.config.debug) { - console.info(`Adding field "${field.body.label}" with api_key "${key}"`); - - console.info( - `Field definition: ${JSON.stringify(field.build(), null, 2)}`, - ); - } - this.fields.push(field); + this.logger.traceJson("Field added successfully", { + fieldKey: key, + totalFields: this.fields.length, + }); return this; } public getField(apiKey: string): Field | undefined { - return this.fields.find((f) => f.build().api_key === apiKey); + this.logger.traceJson("Getting field by API key", { apiKey }); + const field = this.fields.find((f) => f.build().api_key === apiKey); + this.logger.traceJson("Field lookup result", { + apiKey, + found: !!field, + fieldType: field?.constructor.name, + }); + return field; } getNewFieldPosition(): number { - return this.fields.length + 1; + const position = this.fields.length + 1; + this.logger.traceJson("Calculating new field position", { + currentFieldCount: this.fields.length, + newPosition: position, + }); + return position; } public addInteger({ label, body }: IntegerConfig): this { + this.logger.traceJson("Adding integer field", { + label, + position: body?.position, + }); return this.addField( new Integer({ label, @@ -408,6 +277,13 @@ export default abstract class ItemTypeBuilder { } public addSingleLineString({ label, body, options }: SingleLineStringConfig) { + this.logger.traceJson("Adding single line string field", { + label, + position: body?.position, + hasOptions: !!options, + uniqueValidator: + this.type === "block" ? undefined : body?.validators?.unique, + }); return this.addField( new SingleLineString({ label, @@ -426,6 +302,11 @@ export default abstract class ItemTypeBuilder { } public addMarkdown({ label, toolbar, body }: MarkdownConfig) { + this.logger.traceJson("Adding markdown field", { + label, + position: body?.position, + hasToolbar: !!toolbar, + }); return this.addField( new Markdown({ label, @@ -439,6 +320,11 @@ export default abstract class ItemTypeBuilder { } public addWysiwyg({ label, toolbar, body }: WysiwygConfig) { + this.logger.traceJson("Adding wysiwyg field", { + label, + position: body?.position, + hasToolbar: !!toolbar, + }); return this.addField( new Wysiwyg({ label, @@ -452,6 +338,11 @@ export default abstract class ItemTypeBuilder { } public addTextarea({ label, placeholder, body }: TextareaConfig) { + this.logger.traceJson("Adding textarea field", { + label, + position: body?.position, + hasPlaceholder: !!placeholder, + }); return this.addField( new Textarea({ label, @@ -465,6 +356,11 @@ export default abstract class ItemTypeBuilder { } public addHeading({ label, body, options }: SingleLineStringConfig) { + this.logger.traceJson("Adding heading field", { + label, + position: body?.position, + hasOptions: !!options, + }); return this.addSingleLineString({ label, body, @@ -476,6 +372,11 @@ export default abstract class ItemTypeBuilder { } public addText({ label, body, options }: SingleLineStringConfig): this { + this.logger.traceJson("Adding text field", { + label, + position: body?.position, + hasOptions: !!options, + }); return this.addSingleLineString({ label, body, @@ -484,6 +385,11 @@ export default abstract class ItemTypeBuilder { } public addStringRadioGroup({ label, radios, body }: StringRadioGroupConfig) { + this.logger.traceJson("Adding string radio group field", { + label, + position: body?.position, + radioCount: radios?.length || 0, + }); return this.addField( new StringRadioGroup({ label, @@ -497,6 +403,11 @@ export default abstract class ItemTypeBuilder { } public addStringSelect({ label, options, body }: StringSelectConfig) { + this.logger.traceJson("Adding string select field", { + label, + position: body?.position, + optionCount: options?.length || 0, + }); return this.addField( new StringSelect({ label, @@ -510,6 +421,10 @@ export default abstract class ItemTypeBuilder { } public addMultiLineText({ label, body }: MultiLineTextConfig): this { + this.logger.traceJson("Adding multi line text field", { + label, + position: body?.position, + }); return this.addField( new MultiLineText({ label, @@ -522,6 +437,10 @@ export default abstract class ItemTypeBuilder { } public addBoolean({ label, body }: BooleanConfig): this { + this.logger.traceJson("Adding boolean field", { + label, + position: body?.position, + }); return this.addField( new BooleanField({ label, @@ -854,68 +773,81 @@ export default abstract class ItemTypeBuilder { ); } - private static async handlePendingOperation( - apiKey: string, - operationPromise: Promise, - ): Promise { - const operation: PendingOperation = { - promise: operationPromise, - timestamp: Date.now(), - }; + private async syncFields(itemTypeId: string): Promise { + this.logger.traceJson("Starting field synchronization", { + itemTypeId, + itemTypeKey: this.body.api_key, + fieldCount: this.fields.length, + }); - // Register this operation - ItemTypeBuilder.pendingOperations.set(apiKey, operation); + const contextLogger = this.logger.child({ + operation: "syncFields", + itemType: this.body.api_key, + }); - try { - return await operationPromise; - } finally { - // Clean up only if this operation is still the current one - const currentOp = ItemTypeBuilder.pendingOperations.get(apiKey); - if (currentOp && currentOp.promise === operation.promise) { - ItemTypeBuilder.pendingOperations.delete(apiKey); - } + contextLogger.debug(`Starting field sync for itemType: ${itemTypeId}`); - // Clean up any stale operations - ItemTypeBuilder.cleanPendingOperations(); - } - } + this.logger.trace("Fetching existing fields from API"); + const existing = await this.api.call( + () => this.api.client.fields.list(itemTypeId), + 3, + 500, + `fields.list(${itemTypeId})` + ); + const desired = this.fields.map((f) => f.build()); - private async waitForPendingOperation( - apiKey: string, - ): Promise { - const pendingOp = ItemTypeBuilder.pendingOperations.get(apiKey); - if (pendingOp) { - try { - if (this.config.debug) { - console.info(`Waiting for pending operation on "${this.name}"...`); - } - return await pendingOp.promise; - } catch (error) { - // If the pending operation failed, we'll try our own operation - console.warn(`Pending operation for "${this.name}" failed:`, error); - return undefined; - } - } - return undefined; - } + this.logger.traceJson("Field comparison completed", { + existingCount: existing.length, + desiredCount: desired.length, + existingKeys: existing.map((f) => f.api_key), + desiredKeys: desired.map((f) => f.api_key), + }); - private async syncFields(itemTypeId: string): Promise { - const existing = await this.api.call(() => - this.client.fields.list(itemTypeId), + contextLogger.debug( + `Found ${existing.length} existing fields, ${desired.length} desired fields`, ); - const desired = this.fields.map((f) => f.build()); const toCreate = desired.filter( (d) => !existing.some((e) => e.api_key === d.api_key), ); + + this.logger.traceJson("Fields to create identified", { + createCount: toCreate.length, + createKeys: toCreate.map((f) => f.api_key), + }); + + contextLogger.debug( + `Fields to create: ${toCreate.length} - [${toCreate + .map((f) => f.api_key) + .join(", ")}]`, + ); + + this.logger.traceJson("Creating new fields", {}); await Promise.all( toCreate.map(async (fieldDef) => { + const fieldLogger = contextLogger.child({ + field: fieldDef.api_key, + operation: "create", + }); + + this.logger.traceJson("Creating field", { + fieldKey: fieldDef.api_key, + fieldType: fieldDef.field_type, + }); + await executeWithErrorHandling( "create", - () => - this.api.call(() => - this.client.fields.create(itemTypeId, fieldDef), - ), + () => { + fieldLogger.debugJson("Creating field with definition: ", fieldDef); + + return this.api.call( + () => this.api.client.fields.create(itemTypeId, fieldDef), + 3, + 500, + `fields.create(${itemTypeId}, ${fieldDef.label})`, + fieldDef + ); + }, "field", fieldDef, ); @@ -923,24 +855,51 @@ export default abstract class ItemTypeBuilder { ); if (!this.config.overwriteExistingFields) { - console.info( - `Skipping update/delete of existing fields for "${this.name}" because overwriteExistingFields is set to false. Manual changes in the DatoCMS dashboard will not be overwritten.`, + this.logger.traceJson("Skipping field updates", { + reason: "overwriteExistingFields is false", + }); + contextLogger.warn( + "Skipping field updates - overwriteExistingFields is false", ); return; } + this.logger.traceJson("Proceeding with field updates", { + reason: "overwriteExistingFields is true", + }); + contextLogger.debug( + "overwriteExistingFields is true, proceeding with field updates", + ); + // Update existing fields const updatePromises = existing.flatMap((existingField) => { const fieldDef = desired.find((d) => d.api_key === existingField.api_key); if (!fieldDef) return []; + this.logger.traceJson("Preparing field update", { + fieldKey: existingField.api_key, + fieldId: existingField.id, + }); + return [ executeWithErrorHandling( "update", - () => - this.api.call(() => - this.client.fields.update(existingField.id, fieldDef), - ), + () => { + const fieldLogger = contextLogger.child({ + field: existingField.api_key, + operation: "update", + }); + + fieldLogger.debugJson("Updating field with definition: ", fieldDef); + + return this.api.call( + () => this.api.client.fields.update(existingField.id, fieldDef), + 3, + 500, + `fields.update(${existingField.id}, ${fieldDef.label})`, + fieldDef + ); + }, "field", fieldDef, existingField, @@ -948,6 +907,10 @@ export default abstract class ItemTypeBuilder { ]; }); + this.logger.traceJson("Updating existing fields", { + updateCount: updatePromises.length, + }); + contextLogger.debug(`Fields to update: ${updatePromises.length}`); await Promise.all(updatePromises); // Delete fields that are no longer needed @@ -955,168 +918,229 @@ export default abstract class ItemTypeBuilder { (e) => !desired.some((d) => d.api_key === e.api_key), ); + this.logger.traceJson("Fields to delete identified", { + deleteCount: toDelete.length, + deleteKeys: toDelete.map((f) => f.api_key), + }); + + contextLogger.debug( + `Fields to delete: ${toDelete.length} - [${toDelete + .map((f) => f.api_key) + .join(", ")}]`, + ); + + this.logger.traceJson("Deleting obsolete fields", {}); await Promise.all( toDelete.map(async (existingField) => { + const fieldLogger = contextLogger.child({ + field: existingField.api_key, + operation: "delete", + }); + + this.logger.traceJson("Deleting field", { + fieldKey: existingField.api_key, + fieldId: existingField.id, + }); + await executeWithErrorHandling( "delete", - () => - this.api.call(() => this.client.fields.destroy(existingField.id)), + () => { + fieldLogger.debug(`Deleting field with id: ${existingField.id}`); + + return this.api.call( + () => this.api.client.fields.destroy(existingField.id), + 3, + 500, + `fields.destroy(${existingField.id})` + ); + }, "field", undefined, existingField, ); }), ); + + this.logger.traceJson("Field synchronization completed", { + itemTypeId, + totalOperations: + toCreate.length + updatePromises.length + toDelete.length, + }); + contextLogger.debug(`Field sync completed for itemType: ${itemTypeId}`); } public async create(): Promise { + this.logger.traceJson("Creating item type", { + type: this.type, + name: this.name, + apiKey: this.body.api_key, + fieldCount: this.fields.length, + }); + const apiKey = this.body.api_key; - const hash = this.getDefinitionHash(); - - // First check for any pending operations - const pendingResult = await this.waitForPendingOperation(apiKey); - if (pendingResult) { - if (this.config.debug) { - console.info( - `Using result from pending operation for "${this.name}": ${pendingResult}`, - ); - } - return pendingResult; - } - // Check if already in cache with matching hash - const cached = await ItemTypeBuilder.getCache(apiKey); - if (cached && cached.hash === hash) { - console.info( - `Create skipped: "${this.name}" unchanged (id=${cached.id})`, + const contextLogger = this.logger.child({ + operation: "create", + itemType: apiKey, + }); + + try { + this.logger.traceJson("Calling API to create item type", {}); + const item = await this.api.call( + () => this.api.client.itemTypes.create(this.body), + 3, + 500, + `itemTypes.create(${this.body.name})`, + this.body ); - return cached.id; - } - if (this.config.debug) { - console.info(`Creating item type "${this.name}"...`); + this.logger.traceJson("Item type created successfully", { + id: item.id, + apiKey: item.api_key, + }); + contextLogger.debug(`Created itemType with id: ${item.id}`); + + this.logger.traceJson( + "Starting field synchronization for new item type", + {}, + ); + await this.syncFields(item.id); + + this.logger.traceJson("Item type creation completed", { + id: item.id, + apiKey: item.api_key, + }); + return item.id; + } catch (error) { + this.logger.errorJson("Failed to create item type", { + type: this.type, + name: this.name, + apiKey: this.body.api_key, + error: (error as Error).message, + }); + throw error; } + } - // Create a new operation and register it - const operation = async (): Promise => { - try { - // Create the item - const item = await this.api.call(() => - this.client.itemTypes.create(this.body), - ); - await this.syncFields(item.id); - await ItemTypeBuilder.setCache(apiKey, hash, item.id); - console.info(`Created item type "${this.name}" (id=${item.id})`); - - if (this.config.debug) { - console.info( - `Item type "${this.name}" cached (id=${item.id}, hash=${hash})`, - ); - } - return item.id; - } catch (error: unknown) { - if (error instanceof UniquenessError) { - // If the item already exists, we can just return its ID - const existing = await this.api.call(() => - this.client.itemTypes.find(apiKey), - ); - - if (this.config.debug) { - console.info( - `Item type "${this.name}" already exists (id=${existing.id})`, - ); - } + public async update(existingId?: string): Promise { + this.logger.traceJson("Updating item type", { + type: this.type, + name: this.name, + apiKey: this.body.api_key, + fieldCount: this.fields.length, + existingId, + }); - return existing.id; - } + const apiKey = this.body.api_key; - if (error instanceof GenericDatoError) { - console.error( - `Failed to create item type "${this.name}": ${error.message}`, - ); - } - throw error; - } - }; + const contextLogger = this.logger.child({ + operation: "update", + itemType: apiKey, + }); - return ItemTypeBuilder.handlePendingOperation(apiKey, operation()); - } + let existing: any; + if (existingId) { + this.logger.trace("Using provided existing item type ID"); + existing = await this.api.call( + () => this.api.client.itemTypes.find(existingId), + 3, + 500, + `itemTypes.find(${existingId})` + ); + } else { + this.logger.trace("Finding existing item type by name"); + const existingItems = await this.api.call( + () => this.api.client.itemTypes.list(), + 3, + 500, + 'itemTypes.list()' + ); + existing = existingItems.find((item) => item.name === this.body.name); - public async update(): Promise { - const apiKey = this.body.api_key; - const hash = this.getDefinitionHash(); - - // First check for any pending operations - const pending = await this.waitForPendingOperation(apiKey); - if (pending) { - if (this.config.debug) { - console.info( - `Using result from pending operation for "${this.name}": ${pending}`, + if (!existing) { + throw new Error( + `No existing item type found with name "${this.body.name}"`, ); } - return pending; - } - - // Check cache: skip if nothing changed - const cached = await ItemTypeBuilder.getCache(apiKey); - if (cached && cached.hash === hash) { - console.info( - `Update skipped: "${this.name}" unchanged (id=${cached.id})`, - ); - return cached.id; } - if (this.config.debug) { - console.info(`Updating item type "${this.name}"...`); - } + this.logger.traceJson("Found existing item type", { + id: existing.id, + apiKey: existing.api_key, + }); - // Wrap the actual API call in an operation promise - const operation = async (): Promise => { - try { - // Attempt the update - const item = await this.api.call(() => - this.client.itemTypes.update(apiKey, this.body), - ); - await this.syncFields(item.id); - - // Persist to cache - await ItemTypeBuilder.setCache(apiKey, hash, item.id); - - console.info(`Updated item type "${this.name}" (id=${item.id})`); - if (this.config.debug) { - console.info( - `Item type "${this.name}" cached (id=${item.id}, hash=${hash})`, - ); - } - return item.id; - } catch (err: unknown) { - if (err instanceof GenericDatoError) { - console.error( - `Failed to update item type "${this.name}": ${err.message}`, - ); - } - throw err; - } + this.logger.trace("Calling API to update item type"); + const updateBody = { + ...this.body, + hint: this.body.hint ?? null, }; - // Register & return a single in‐flight promise - return ItemTypeBuilder.handlePendingOperation(apiKey, operation()); + this.logger.traceJson("Update body prepared", { + updateBody, + }); + + const item = await this.api.call( + () => this.api.client.itemTypes.update(existing.id, updateBody), + 3, + 500, + `itemTypes.update(${existing.id}, ${updateBody.name})`, + updateBody + ); + + this.logger.traceJson("Item type updated successfully", { + id: item.id, + apiKey: item.api_key, + }); + contextLogger.debug(`Updated itemType with id: ${item.id}`); + + this.logger.trace("Starting field synchronization for updated item type"); + await this.syncFields(item.id); + + return item.id; } public async upsert(): Promise { - if (this.config.debug) { - console.info(`Upserting item type \"${this.name}\"...`); - } + this.logger.traceJson("Upserting item type", { + type: this.type, + name: this.name, + apiKey: this.body.api_key, + fieldCount: this.fields.length, + }); + + const apiKey = this.body.api_key; + + const contextLogger = this.logger.child({ + operation: "upsert", + itemType: apiKey, + }); + + contextLogger.debug("Upserting itemType"); try { - await this.api.call(() => this.client.itemTypes.find(this.body.api_key)); + this.logger.trace("Checking if item type exists by name"); + const existingItems = await this.api.call( + () => this.api.client.itemTypes.list(), + 3, + 500, + 'itemTypes.list()' + ); + const existingItem = existingItems.find( + (item) => item.name === this.body.name, + ); - return this.update(); - } catch (error: unknown) { - if (error instanceof NotFoundError) { + if (existingItem) { + contextLogger.debug("ItemType exists by name, proceeding with update"); + return this.update(existingItem.id); + } else { + contextLogger.debug( + "ItemType not found by name, proceeding with creation", + ); return this.create(); } - + } catch (error: unknown) { + this.logger.errorJson("Error during upsert operation", { + error: (error as Error).message, + }); throw error; } } diff --git a/src/ModelBuilder.ts b/src/ModelBuilder.ts index 5d8007a..36177d7 100644 --- a/src/ModelBuilder.ts +++ b/src/ModelBuilder.ts @@ -1,17 +1,23 @@ import ItemTypeBuilder, { type ItemTypeBuilderBody, - type ItemTypeBuilderConfig, type ItemTypeBuilderType, } from "./ItemTypeBuilder"; +import type { DatoBuilderConfig } from "./types/DatoBuilderConfig"; + +type ModelBuilderOptions = { + name: string; + body?: Omit; + config: Required; +}; export default class ModelBuilder extends ItemTypeBuilder { - public type: ItemTypeBuilderType = "model"; + public override type: ItemTypeBuilderType = "model"; - constructor( - name: string, - body?: Omit, - config?: ItemTypeBuilderConfig, - ) { - super("model", { ...body, name }, config); + constructor({ name, body, config }: ModelBuilderOptions) { + super({ + type: "model", + body: { ...body, name }, + config, + }); } } diff --git a/src/Validators/FormatValidator.ts b/src/Validators/FormatValidator.ts index 5dda7c0..fab1e9b 100644 --- a/src/Validators/FormatValidator.ts +++ b/src/Validators/FormatValidator.ts @@ -34,7 +34,7 @@ export default class FormatValidator implements Validator { if (custom_pattern) { return { - custom_pattern: custom_pattern.toString(), + custom_pattern: custom_pattern.source, description, }; } diff --git a/tests/validators/required-seo-fields-validator.test.ts b/src/Validators/RequiredSeoFieldsValidator.test.ts similarity index 100% rename from tests/validators/required-seo-fields-validator.test.ts rename to src/Validators/RequiredSeoFieldsValidator.test.ts diff --git a/src/Validators/Validators.ts b/src/Validators/Validators.ts index 2795d4a..6385d25 100644 --- a/src/Validators/Validators.ts +++ b/src/Validators/Validators.ts @@ -1,3 +1,4 @@ +import { ConsoleLogger, LogLevel } from "../logger"; import type { DateRangeValidatorConfig } from "./DateRangeValidator"; import DateRangeValidator from "./DateRangeValidator"; import DateTimeRangeValidator, { @@ -87,8 +88,14 @@ export type ValidatorConfig = Partial<{ export default class Validators { private validators: Validator[] = []; + private logger: ConsoleLogger; constructor(config: ValidatorConfig = {}) { + this.logger = new ConsoleLogger(LogLevel.ERROR); + this.logger.traceJson("Initializing validators", { + configKeys: Object.keys(config), + }); + const validatorMap: { [key: string]: { ValidatorClass: new ( @@ -197,22 +204,35 @@ export default class Validators { ] of Object.entries(validatorMap)) { if (validatorConfig !== undefined) { try { + this.logger.traceJson("Creating validator", { + key, + config: validatorConfig, + }); const validator = new ValidatorClass(validatorConfig); this.validators.push(validator); } catch (error) { - console.error(`Error creating validator for ${key}: ${error}`); + this.logger.error(`Error creating validator for ${key}: ${error}`); } } } + + this.logger.traceJson("Validators initialization completed", { + validatorCount: this.validators.length, + }); } build(): Record { - return this.validators.reduce( + this.logger.trace("Building validators configuration"); + const result = this.validators.reduce( (result, validator) => { result[validator.key] = validator.build(); return result; }, {} as Record, ); + this.logger.traceJson("Validators configuration built", { + validatorCount: Object.keys(result).length, + }); + return result; } } diff --git a/src/cache/CacheManager.test.ts b/src/cache/CacheManager.test.ts new file mode 100644 index 0000000..00bb63b --- /dev/null +++ b/src/cache/CacheManager.test.ts @@ -0,0 +1,444 @@ +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { CacheManager } from "./CacheManager"; + +// Mock fs module +jest.mock("node:fs", () => ({ + promises: { + readFile: jest.fn(), + writeFile: jest.fn(), + mkdir: jest.fn(), + }, +})); + +// Mock path module +jest.mock("node:path", () => ({ + dirname: jest.fn(), +})); + +const mockFs = fs as jest.Mocked; +const mockPath = path as jest.Mocked; + +describe("CacheManager", () => { + const cachePath = "/test/cache.json"; + const cacheDir = "/test"; + + beforeEach(() => { + jest.clearAllMocks(); + mockPath.dirname.mockReturnValue(cacheDir); + }); + + describe("constructor", () => { + it("should create instance with default options", () => { + const cache = new CacheManager(cachePath); + expect(cache.isSkippingReads()).toBe(false); + }); + + it("should create instance with skipReads option", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.isSkippingReads()).toBe(true); + }); + }); + + describe("initialize", () => { + it("should skip initialization when skipReads is true", async () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + await cache.initialize(); + + expect(mockFs.mkdir).not.toHaveBeenCalled(); + expect(mockFs.readFile).not.toHaveBeenCalled(); + }); + + it("should create cache directory and load from file", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [["key1", { hash: "hash1", id: "id1" }]]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + + await cache.initialize(); + + expect(mockFs.mkdir).toHaveBeenCalledWith(cacheDir, { recursive: true }); + expect(mockFs.readFile).toHaveBeenCalledWith(cachePath, "utf8"); + expect(cache.get("key1")).toEqual({ hash: "hash1", id: "id1" }); + }); + + it("should handle missing cache file", async () => { + const cache = new CacheManager(cachePath); + const error = new Error("File not found") as any; + error.code = "ENOENT"; + + mockFs.readFile.mockRejectedValue(error); + + await cache.initialize(); + + expect(cache.size()).toBe(0); + }); + + it("should handle corrupted cache file", async () => { + const cache = new CacheManager(cachePath); + + mockFs.readFile.mockResolvedValue("invalid json"); + + await cache.initialize(); + + expect(cache.size()).toBe(0); + }); + + it("should handle object format cache data", async () => { + const cache = new CacheManager(cachePath); + const cacheData = { key1: { hash: "hash1", id: "id1" } }; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + + await cache.initialize(); + + expect(cache.get("key1")).toEqual({ hash: "hash1", id: "id1" }); + }); + }); + + describe("get", () => { + it("should return undefined when skipReads is true", async () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.get("key1")).toBeUndefined(); + }); + + it("should return cached item", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [["key1", { hash: "hash1", id: "id1" }]]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.get("key1")).toEqual({ hash: "hash1", id: "id1" }); + }); + + it("should return undefined for non-existent key", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify([])); + await cache.initialize(); + + expect(cache.get("nonexistent")).toBeUndefined(); + }); + }); + + describe("set", () => { + it("should be op when skipReads is true", async () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + await cache.set("key1", { hash: "hash1", id: "id1" }); + + expect(mockFs.writeFile).toHaveBeenCalled(); + expect(cache.get("key1")).toEqual({ hash: "hash1", id: "id1" }); + }); + + it("should set item and persist to file", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify([])); + await cache.initialize(); + + await cache.set("key1", { hash: "hash1", id: "id1" }); + + expect(cache.get("key1")).toEqual({ hash: "hash1", id: "id1" }); + expect(mockFs.writeFile).toHaveBeenCalledWith( + cachePath, + JSON.stringify([["key1", { hash: "hash1", id: "id1" }]], null, 2), + "utf8", + ); + }); + + it("should handle multiple concurrent writes", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify([])); + await cache.initialize(); + + // Start multiple set operations concurrently + const promises = [ + cache.set("key1", { hash: "hash1", id: "id1" }), + cache.set("key2", { hash: "hash2", id: "id2" }), + cache.set("key3", { hash: "hash3", id: "id3" }), + ]; + + await Promise.all(promises); + + expect(cache.size()).toBe(3); + expect(mockFs.writeFile).toHaveBeenCalled(); + }); + }); + + describe("has", () => { + it("should return false when skipReads is true", async () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.has("key1")).toBe(false); + }); + + it("should return true for existing key", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [["key1", { hash: "hash1", id: "id1" }]]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.has("key1")).toBe(true); + }); + + it("should return false for non-existent key", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify([])); + await cache.initialize(); + + expect(cache.has("nonexistent")).toBe(false); + }); + }); + + describe("delete", () => { + it("should return false when skipReads is true", async () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + const result = await cache.delete("key1"); + expect(result).toBe(false); + }); + + it("should delete existing key and persist", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [["key1", { hash: "hash1", id: "id1" }]]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + const result = await cache.delete("key1"); + + expect(result).toBe(true); + expect(cache.has("key1")).toBe(false); + expect(mockFs.writeFile).toHaveBeenCalledWith( + cachePath, + JSON.stringify([], null, 2), + "utf8", + ); + }); + + it("should return false for non-existent key", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify([])); + await cache.initialize(); + + const result = await cache.delete("nonexistent"); + expect(result).toBe(false); + }); + }); + + describe("clear", () => { + it("should op when skipReads is true", async () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + await cache.clear(); + + expect(mockFs.writeFile).toHaveBeenCalledWith( + cachePath, + JSON.stringify([], null, 2), + "utf8", + ); + expect(cache.size()).toBe(0); + }); + + it("should clear all items and persist", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + await cache.clear(); + + expect(cache.size()).toBe(0); + expect(mockFs.writeFile).toHaveBeenCalledWith( + cachePath, + JSON.stringify([], null, 2), + "utf8", + ); + }); + }); + + describe("keys", () => { + it("should return empty array when skipReads is true", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.keys()).toEqual([]); + }); + + it("should return all keys", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.keys()).toEqual(["key1", "key2"]); + }); + }); + + describe("values", () => { + it("should return empty array when skipReads is true", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.values()).toEqual([]); + }); + + it("should return all values", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.values()).toEqual([ + { hash: "hash1", id: "id1" }, + { hash: "hash2", id: "id2" }, + ]); + }); + }); + + describe("size", () => { + it("should return 0 when skipReads is true", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.size()).toBe(0); + }); + + it("should return correct size", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.size()).toBe(2); + }); + }); + + describe("entries", () => { + it("should return empty array when skipReads is true", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.entries()).toEqual([]); + }); + + it("should return all entries", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.entries()).toEqual([ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]); + }); + }); + + describe("findByHash", () => { + it("should return empty array when skipReads is true", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.findByHash("hash1")).toEqual([]); + }); + + it("should find items by hash", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash1", id: "id2" }], + ["key3", { hash: "hash2", id: "id3" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + const result = cache.findByHash("hash1"); + expect(result).toEqual([ + { hash: "hash1", id: "id1" }, + { hash: "hash1", id: "id2" }, + ]); + }); + }); + + describe("findById", () => { + it("should return undefined when skipReads is true", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.findById("id1")).toBeUndefined(); + }); + + it("should find item by id", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.findById("id2")).toEqual({ hash: "hash2", id: "id2" }); + }); + + it("should return undefined for non-existent id", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify([])); + await cache.initialize(); + + expect(cache.findById("nonexistent")).toBeUndefined(); + }); + }); + + describe("getActualSize", () => { + it("should return 0 when skipReads is true", () => { + const cache = new CacheManager(cachePath, { skipReads: true }); + expect(cache.getActualSize()).toBe(0); + }); + + it("should return actual size", async () => { + const cache = new CacheManager(cachePath); + const cacheData = [ + ["key1", { hash: "hash1", id: "id1" }], + ["key2", { hash: "hash2", id: "id2" }], + ]; + + mockFs.readFile.mockResolvedValue(JSON.stringify(cacheData)); + await cache.initialize(); + + expect(cache.getActualSize()).toBe(2); + }); + }); + + describe("error handling", () => { + it("should handle write errors in queue", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify([])); + await cache.initialize(); + + const writeError = new Error("Write failed"); + mockFs.writeFile.mockRejectedValue(writeError); + + await expect( + cache.set("key1", { hash: "hash1", id: "id1" }), + ).rejects.toThrow("Write failed"); + }); + + it("should handle invalid cache file format", async () => { + const cache = new CacheManager(cachePath); + mockFs.readFile.mockResolvedValue(JSON.stringify("invalid")); + + await cache.initialize(); + + expect(cache.size()).toBe(0); + }); + }); +}); diff --git a/src/cache/CacheManager.ts b/src/cache/CacheManager.ts new file mode 100644 index 0000000..7a39462 --- /dev/null +++ b/src/cache/CacheManager.ts @@ -0,0 +1,241 @@ +import { promises as fs } from "node:fs"; +import path from "node:path"; + +type CacheData = { + hash: string; + id: string; +}; + +export class CacheManager { + private items: Map; + + private readonly cachePath: string; + private readonly skipReads: boolean; + + private writeQueue: Array<{ + data: CacheData; + resolve: Function; + reject: Function; + }> = []; + private isWriting: boolean = false; + + constructor(cachePath: string, options: { skipReads?: boolean } = {}) { + this.cachePath = cachePath; + this.skipReads = options.skipReads || false; + // Always initialize the Map for in-memory caching + this.items = new Map(); + } + + /** + * Initialize the cache by loading existing data from file + * Skips loading if skipReads is enabled + */ + async initialize(): Promise { + // Skip loading from file if skipReads is enabled, but keep in-memory cache + if (this.skipReads) { + return; + } + + try { + await this.ensureCacheDirectoryExists(); + await this.loadFromFile(); + } catch (_error) { + // Continue with empty cache if file doesn't exist or is corrupted + this.items = new Map(); + } + } + + /** + * Get an item from the cache + */ + get(key: string): CacheData | undefined { + return this.items.get(key); + } + + /** + * Set an item in the cache + */ + async set(key: string, data: CacheData): Promise { + this.items.set(key, data); + return this.queueWrite(data); + } + + /** + * Check if an item exists in the cache + */ + has(key: string): boolean { + return this.items.has(key); + } + + /** + * Delete an item from the cache + */ + async delete(key: string): Promise { + const existed = this.items.delete(key); + if (existed) { + await this.persistToFile(); + } + return existed; + } + + /** + * Clear all items from the cache + */ + async clear(): Promise { + this.items.clear(); + await this.persistToFile(); + } + + /** + * Get all cache keys + */ + keys(): string[] { + return Array.from(this.items.keys()); + } + + /** + * Get all cache values + */ + values(): CacheData[] { + return Array.from(this.items.values()); + } + + /** + * Get cache size + */ + size(): number { + return this.items.size; + } + + /** + * Get cache entries as array of [key, value] pairs + */ + entries(): [string, CacheData][] { + return Array.from(this.items.entries()); + } + + /** + * Find items by hash + */ + findByHash(hash: string): CacheData[] { + return this.values().filter((item) => item.hash === hash); + } + + /** + * Find item by id + */ + findById(id: string): CacheData | undefined { + return this.values().find((item) => item.id === id); + } + + /** + * Check if cache reads are being skipped + */ + isSkippingReads(): boolean { + return this.skipReads; + } + + /** + * Get actual cache size (always returns current in-memory size) + */ + getActualSize(): number { + return this.items.size; + } + + /** + * Load cache data from file + */ + private async loadFromFile(): Promise { + try { + const data = await fs.readFile(this.cachePath, "utf8"); + const parsed = JSON.parse(data); + + if (Array.isArray(parsed)) { + // Handle array format: [key, value] pairs + this.items = new Map(parsed); + } else if (typeof parsed === "object" && parsed !== null) { + // Handle object format + this.items = new Map(Object.entries(parsed)); + } else { + throw new Error("Invalid cache file format"); + } + } catch (error) { + if ((error as any).code === "ENOENT") { + // File doesn't exist, start with empty cache + this.items = new Map(); + } else { + throw error; + } + } + } + + /** + * Ensure the cache directory exists + */ + private async ensureCacheDirectoryExists(): Promise { + const dir = path.dirname(this.cachePath); + await fs.mkdir(dir, { recursive: true }); + } + + /** + * Queue a write operation to avoid concurrent writes + */ + private async queueWrite(data: CacheData): Promise { + return new Promise((resolve, reject) => { + this.writeQueue.push({ data, resolve, reject }); + this.processWriteQueue(); + }); + } + + /** + * Process the write queue + */ + private async processWriteQueue(): Promise { + if (this.isWriting || this.writeQueue.length === 0) { + return; + } + + this.isWriting = true; + + try { + await this.persistToFile(); + + // Resolve all queued promises + const queue = [...this.writeQueue]; + this.writeQueue = []; + for (const { resolve } of queue) { + resolve(); + } + } catch (error) { + // Reject all queued promises + const queue = [...this.writeQueue]; + this.writeQueue = []; + for (const { reject } of queue) { + reject(error); + } + } finally { + this.isWriting = false; + + // Process any items that were queued while writing + if (this.writeQueue.length > 0) { + await this.processWriteQueue(); + } + } + } + + /** + * Persist the current cache state to file + */ + private async persistToFile(): Promise { + await this.ensureCacheDirectoryExists(); + + // Convert Map to array of [key, value] pairs for JSON serialization + const dataToWrite = Array.from(this.items.entries()); + + await fs.writeFile( + this.cachePath, + JSON.stringify(dataToWrite, null, 2), + "utf8", + ); + } +} diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..94fa11e --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,317 @@ +import os from "node:os"; +import path from "node:path"; +import { Command } from "@commander-js/extra-typings"; +import { CacheManager } from "./cache/CacheManager"; +import { RunCommand } from "./commands/run/RunCommand"; +import { ConfigParser } from "./config/ConfigParser"; +import { DatoCmsSync } from "./DatoCmsSync"; +import { ConsoleLogger, LogLevel } from "./logger"; +import type { DatoBuilderConfig } from "./types/DatoBuilderConfig"; + +interface BaseCommandOptions { + config: Required; + cache: CacheManager; + logger: ConsoleLogger; +} + +interface BuildCommandOptions { + enableDeletion?: boolean; + skipDeletionConfirmation?: boolean; + concurrency?: number; +} + +interface SyncCommandOptions { + dryRun?: boolean; + force?: boolean; +} + +export class DatoBuilderCLI { + private readonly config: Required; + private readonly cache: CacheManager; + private readonly logger: ConsoleLogger; + + constructor(options: BaseCommandOptions) { + this.config = options.config; + this.cache = options.cache; + this.logger = options.logger; + } + + /** + * Execute the build command + */ + public async build(options: BuildCommandOptions = {}): Promise { + this.logger.trace("Starting build command execution"); + await new RunCommand({ + config: this.config, + cache: this.cache, + logger: this.logger, + enableDeletion: options.enableDeletion, + skipDeletionConfirmation: options.skipDeletionConfirmation, + concurrency: options.concurrency ?? 1, + }).execute(); + this.logger.trace("Build command execution completed"); + } + + /** + * Clear all caches + */ + public async clearCache(): Promise { + this.logger.trace("Starting cache clear operation"); + + await this.cache.clear(); + + this.logger.success("All caches cleared!"); + this.logger.trace("Cache clear operation completed"); + } + + /** + * Sync from DatoCMS to local files + */ + public async sync(options: SyncCommandOptions = {}): Promise { + this.logger.trace("Starting sync from DatoCMS"); + + const syncService = new DatoCmsSync({ + config: this.config, + cache: this.cache, + logger: this.logger, + }); + + // Ask for confirmation unless force is enabled + if (!options.force && !options.dryRun) { + const confirmed = await syncService.confirmSync(); + if (!confirmed) { + this.logger.info("Sync cancelled by user"); + return; + } + } + + await syncService.sync({ + dryRun: options.dryRun, + force: options.force, + }); + + this.logger.trace("Sync operation completed"); + } +} + +// Type definitions for options +type GlobalOptions = { + debug: boolean; + verbose: boolean; + quiet: boolean; + cache: boolean; +}; + +type BuildOptions = { + skipDeletion: boolean; + skipDeletionConfirmation: boolean; + concurrent?: boolean; + concurrency?: number; + autoConcurrency?: boolean; +}; + +type SyncOptions = { + dryRun: boolean; + force: boolean; +}; + +// Setup Commander CLI +async function setupCLI(): Promise { + const program = new Command() + .name("dato-builder") + .description("DatoCMS Builder CLI") + .version(process.env.npm_package_version || "0.0.0") + .option("-n, --no-cache", "Disable cache usage") + .option("-d, --debug", "Output information useful for debugging.", false) + .option("-v, --verbose", "Display even finer-grained trace logs.", false) + .option("-q, --quiet", "Only display errors.", false); + + function getLogLevelFromOptions(options: GlobalOptions): LogLevel { + if (options.debug) { + return LogLevel.DEBUG; + } else if (options.quiet) { + return LogLevel.ERROR; + } else if (options.verbose) { + return LogLevel.TRACE; + } else { + return LogLevel.INFO; + } + } + + async function initializeCLI( + globalOptions: GlobalOptions, + ): Promise { + const level = getLogLevelFromOptions(globalOptions); + + const logger = new ConsoleLogger( + level, + {}, + { + timestamp: level === LogLevel.TRACE, + prefix: level === LogLevel.TRACE ? "dato-builder" : undefined, + prettyJson: true, + }, + ); + const configParser = new ConfigParser(logger); + const cache = new CacheManager( + path.join(process.cwd(), ".dato-builder-cache", "item-types.json"), + { + skipReads: !globalOptions.cache, + }, + ); + + const config = await configParser.loadConfig(); + + // Override log level from config if CLI options are provided + if (globalOptions.debug || globalOptions.verbose || globalOptions.quiet) { + logger.setLevel(level); + config.logLevel = level; + } else { + logger.setLevel(config.logLevel); + } + + await cache.initialize(); + + return new DatoBuilderCLI({ + config, + cache, + logger, + }); + } + + // Build command + program + .command("build") + .description("Build DatoCMS types and blocks") + .option( + "--skip-deletion", + "Skip deletion detection and removal of orphaned items", + false, + ) + .option( + "--skip-deletion-confirmation", + "Skip confirmation prompts for deletions", + false, + ) + .option( + "--concurrent", + "Enable concurrent builds (default concurrency: 3)", + false, + ) + .option( + "--concurrency ", + "Set the concurrency level for builds (implies --concurrent)", + parseInt, + ) + .option( + "--auto-concurrency", + "Automatically determine and set concurrency based on CPU cores", + false, + ) + .action(async (options: BuildOptions, command) => { + try { + const globalOptions = command.optsWithGlobals(); + const cli = await initializeCLI({ + debug: globalOptions.debug, + verbose: globalOptions.verbose, + quiet: globalOptions.quiet, + cache: globalOptions.cache, + }); + let concurrency = options.concurrency; + + // Handle concurrency options + if (options.autoConcurrency) { + concurrency = Math.max(1, os.cpus().length - 1); + console.log(`Auto-determined concurrency level: ${concurrency}`); + } else if (options.concurrent && !options.concurrency) { + // Use default concurrent level if --concurrent is specified without --concurrency + concurrency = 3; + } else if ( + !options.concurrent && + !options.concurrency && + !options.autoConcurrency + ) { + // Default to sequential + concurrency = 1; + } + await cli.build({ + enableDeletion: !options.skipDeletion, + skipDeletionConfirmation: options.skipDeletionConfirmation, + concurrency, + }); + } catch (error) { + const logger = new ConsoleLogger(LogLevel.ERROR); + logger.error(`Build failed: ${(error as Error).message}`); + process.exit(1); + } + }); + + // Clear cache command + program + .command("clear-cache") + .description("Clear all caches") + .action(async (_options, command) => { + try { + const globalOptions = command.optsWithGlobals(); + const cli = await initializeCLI({ + debug: globalOptions.debug, + verbose: globalOptions.verbose, + quiet: globalOptions.quiet, + cache: globalOptions.cache, + }); + await cli.clearCache(); + } catch (error) { + const logger = new ConsoleLogger(LogLevel.ERROR); + logger.error(`Cache clear failed: ${(error as Error).message}`); + process.exit(1); + } + }); + + // Sync command + program + .command("sync") + .description("Sync blocks and models from DatoCMS to local files") + .option( + "--dry-run", + "Show what would be synced without making any changes", + false, + ) + .option("--force", "Force sync all items, ignoring cache", false) + .action(async (options: SyncOptions, command) => { + try { + const globalOptions = command.optsWithGlobals(); + const cli = await initializeCLI({ + debug: globalOptions.debug, + verbose: globalOptions.verbose, + quiet: globalOptions.quiet, + cache: globalOptions.cache, + }); + + await cli.sync({ + dryRun: options.dryRun, + force: options.force, + }); + } catch (error) { + const logger = new ConsoleLogger(LogLevel.ERROR); + logger.error(`Sync failed: ${(error as Error).message}`); + process.exit(1); + } + }); + + await program.parseAsync(process.argv); +} + +// Main execution +if (require.main === module) { + setupCLI().catch((error: unknown) => { + const logger = new ConsoleLogger(LogLevel.ERROR); + if (error instanceof Error) { + logger.error(`CLI initialization failed: ${error.message}`); + } else { + logger.error("CLI initialization failed with an unknown error"); + } + process.exit(1); + }); +} + +export { setupCLI }; diff --git a/src/commands/run/BuildExecutor.test.ts b/src/commands/run/BuildExecutor.test.ts new file mode 100644 index 0000000..e34ad44 --- /dev/null +++ b/src/commands/run/BuildExecutor.test.ts @@ -0,0 +1,233 @@ +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { createMockLogger } from "@tests/utils/mockLogger"; +import { BuildExecutor } from "./BuildExecutor"; +import type { ItemBuilder } from "./ItemBuilder"; +import type { FileInfo } from "./types"; + +// Mock ItemBuilder +const mockItemBuilder = { + buildItem: jest.fn(), +} as unknown as jest.Mocked; + +const mockLogger = createMockLogger(); + +describe("BuildExecutor", () => { + let buildExecutor: BuildExecutor; + + beforeEach(() => { + buildExecutor = new BuildExecutor(mockItemBuilder, mockLogger); + jest.clearAllMocks(); + }); + + describe("executeBuild", () => { + it("should execute build for all items in order", async () => { + const fileMap = new Map([ + [ + "block:item1", + { + name: "item1", + type: "block", + filePath: "/path/to/item1.ts", + dependencies: new Set(), + }, + ], + [ + "model:item2", + { + name: "item2", + type: "model", + filePath: "/path/to/item2.ts", + dependencies: new Set(), + }, + ], + ]); + + const buildOrder = ["block:item1", "model:item2"]; + + mockItemBuilder.buildItem.mockResolvedValueOnce({ + id: "id1", + fromCache: false, + }); + mockItemBuilder.buildItem.mockResolvedValueOnce({ + id: "id2", + fromCache: true, + }); + + const results = await buildExecutor.executeBuild(fileMap, buildOrder); + + expect(results).toEqual([ + { + name: "item1", + type: "block", + fromCache: false, + success: true, + }, + { + name: "item2", + type: "model", + fromCache: true, + success: true, + }, + ]); + + expect(mockItemBuilder.buildItem).toHaveBeenCalledTimes(2); + }); + + it("should handle build errors gracefully", async () => { + const fileMap = new Map([ + [ + "block:item1", + { + name: "item1", + type: "block", + filePath: "/path/to/item1.ts", + dependencies: new Set(), + }, + ], + ]); + + const buildOrder = ["block:item1"]; + const buildError = new Error("Build failed"); + + mockItemBuilder.buildItem.mockRejectedValueOnce(buildError); + + const results = await buildExecutor.executeBuild(fileMap, buildOrder); + + expect(results).toEqual([ + { + name: "item1", + type: "block", + fromCache: false, + success: false, + error: buildError, + }, + ]); + }); + + it("should throw error when file info is not found", async () => { + const fileMap = new Map(); + const buildOrder = ["block:missing"]; + + await expect( + buildExecutor.executeBuild(fileMap, buildOrder), + ).rejects.toThrow("File info not found for: block:missing"); + }); + + it("should handle empty file map and build order", async () => { + const fileMap = new Map(); + const buildOrder: string[] = []; + + const results = await buildExecutor.executeBuild(fileMap, buildOrder); + + expect(results).toEqual([]); + expect(mockItemBuilder.buildItem).not.toHaveBeenCalled(); + }); + }); + + describe("getOrBuildItem", () => { + it("should build item when not in cache", async () => { + const fileInfo: FileInfo = { + name: "item1", + type: "block", + filePath: "/path/to/item1.ts", + dependencies: new Set(), + }; + + const expectedResult = { id: "id1", fromCache: false }; + mockItemBuilder.buildItem.mockResolvedValueOnce(expectedResult); + + const result = await buildExecutor.getOrBuildItem( + "block:item1", + fileInfo, + ); + + expect(result).toBe(expectedResult); + expect(mockItemBuilder.buildItem).toHaveBeenCalledWith(fileInfo); + }); + + it("should return existing promise if build is in progress", async () => { + const fileInfo: FileInfo = { + name: "item1", + type: "block", + filePath: "/path/to/item1.ts", + dependencies: new Set(), + }; + + const expectedResult = { id: "id1", fromCache: false }; + mockItemBuilder.buildItem.mockResolvedValueOnce(expectedResult); + + // Start first build + const firstBuildPromise = buildExecutor.getOrBuildItem( + "block:item1", + fileInfo, + ); + + // Start second build for same item + const secondBuildPromise = buildExecutor.getOrBuildItem( + "block:item1", + fileInfo, + ); + + const [firstResult, secondResult] = await Promise.all([ + firstBuildPromise, + secondBuildPromise, + ]); + + expect(firstResult).toBe(expectedResult); + expect(secondResult).toBe(expectedResult); + // Should only call buildItem once, not twice + expect(mockItemBuilder.buildItem).toHaveBeenCalledTimes(1); + }); + + it("should clean up promise after completion", async () => { + const fileInfo: FileInfo = { + name: "item1", + type: "block", + filePath: "/path/to/item1.ts", + dependencies: new Set(), + }; + + const expectedResult = { id: "id1", fromCache: false }; + mockItemBuilder.buildItem.mockResolvedValueOnce(expectedResult); + + await buildExecutor.getOrBuildItem("block:item1", fileInfo); + + // Reset mock to track new calls + mockItemBuilder.buildItem.mockClear(); + mockItemBuilder.buildItem.mockResolvedValueOnce(expectedResult); + + // Should create new promise, not reuse old one + await buildExecutor.getOrBuildItem("block:item1", fileInfo); + + expect(mockItemBuilder.buildItem).toHaveBeenCalledTimes(1); + }); + + it("should clean up promise even when build fails", async () => { + const fileInfo: FileInfo = { + name: "item1", + type: "block", + filePath: "/path/to/item1.ts", + dependencies: new Set(), + }; + + const buildError = new Error("Build failed"); + mockItemBuilder.buildItem.mockRejectedValueOnce(buildError); + + await expect( + buildExecutor.getOrBuildItem("block:item1", fileInfo), + ).rejects.toThrow("Build failed"); + + // Reset mock and try again + mockItemBuilder.buildItem.mockClear(); + mockItemBuilder.buildItem.mockResolvedValueOnce({ + id: "id1", + fromCache: false, + }); + + // Should create new promise after error + await buildExecutor.getOrBuildItem("block:item1", fileInfo); + + expect(mockItemBuilder.buildItem).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/src/commands/run/BuildExecutor.ts b/src/commands/run/BuildExecutor.ts new file mode 100644 index 0000000..db08258 --- /dev/null +++ b/src/commands/run/BuildExecutor.ts @@ -0,0 +1,149 @@ +import type { ConsoleLogger } from "../../logger"; +import { ItemBuildError, type ItemBuilder } from "./ItemBuilder"; +import type { BuildResult, FileInfo } from "./types"; + +export class BuildExecutor { + private readonly buildPromises = new Map< + string, + Promise<{ id: string; fromCache: boolean }> + >(); + + constructor( + private readonly itemBuilder: ItemBuilder, + private readonly logger: ConsoleLogger, + ) { + this.logger.trace("Initializing BuildExecutor"); + } + + async executeBuild( + fileMap: Map, + buildOrder: string[], + ): Promise { + this.logger.traceJson("Starting build execution", { + fileCount: fileMap.size, + buildOrderLength: buildOrder.length, + }); + this.logger.debug(`Build order determined: ${buildOrder.join(" -> ")}`); + + this.logger.trace("Creating build promises for all items"); + const buildPromises = buildOrder.map( + async (fileKey): Promise => { + this.logger.traceJson("Processing build item", { fileKey }); + + const fileInfo = fileMap.get(fileKey); + if (!fileInfo) { + this.logger.errorJson("File info not found", { fileKey }); + throw new Error(`File info not found for: ${fileKey}`); + } + + this.logger.traceJson("Building item", { + type: fileInfo.type, + name: fileInfo.name, + filePath: fileInfo.filePath, + }); + + try { + const result = await this.getOrBuildItem(fileKey, fileInfo); + this.logger.traceJson("Item built successfully", { + type: fileInfo.type, + name: fileInfo.name, + id: result.id, + fromCache: result.fromCache, + }); + + return { + name: fileInfo.name, + type: fileInfo.type, + fromCache: result.fromCache, + success: true, + }; + } catch (error: unknown) { + const err = error as Error; + this.logger.errorJson( + `Failed to build ${fileInfo.type} "${fileInfo.name}"`, + err, + ); + this.logger.traceJson("Build failed", { + type: fileInfo.type, + name: fileInfo.name, + error: err.message, + stack: err.stack, + }); + + return { + name: fileInfo.name, + type: fileInfo.type, + fromCache: false, + success: false, + error: err, + }; + } + }, + ); + + this.logger.trace("Executing all build promises"); + const results = await Promise.all(buildPromises); + + this.logger.traceJson("Build execution completed", { + totalResults: results.length, + successful: results.filter((r) => r.success).length, + failed: results.filter((r) => !r.success).length, + fromCache: results.filter((r) => r.fromCache).length, + }); + + return results; + } + + public async getOrBuildItem( + fileKey: string, + fileInfo: FileInfo, + ): Promise<{ id: string; fromCache: boolean }> { + this.logger.traceJson("Getting or building item", { + fileKey, + type: fileInfo.type, + name: fileInfo.name, + }); + + // Return existing promise if currently building + const existingPromise = this.buildPromises.get(fileKey); + if (existingPromise) { + this.logger.traceJson("Found existing build promise", { fileKey }); + return existingPromise; + } + + this.logger.traceJson("Creating new build promise", { fileKey }); + // Create new build promise + const buildPromise = this.itemBuilder.buildItem(fileInfo); + this.buildPromises.set(fileKey, buildPromise); + + try { + const result = await buildPromise; + this.logger.traceJson("Build promise completed", { + fileKey, + id: result.id, + fromCache: result.fromCache, + }); + return result; + } catch (error: unknown) { + if (error instanceof ItemBuildError) { + this.logger.errorJson(`Error building item ${fileKey}`, error); + } else { + this.logger.errorJson( + `Unexpected error building item ${fileKey}`, + error, + ); + } + + this.logger.traceJson("Build promise failed", { + fileKey, + error: (error as Error).message, + stack: (error as Error).stack, + }); + + return Promise.reject(error); + } finally { + this.logger.traceJson("Cleaning up build promise", { fileKey }); + this.buildPromises.delete(fileKey); + } + } +} diff --git a/src/commands/run/DeletionDetector.test.ts b/src/commands/run/DeletionDetector.test.ts new file mode 100644 index 0000000..ab92e29 --- /dev/null +++ b/src/commands/run/DeletionDetector.test.ts @@ -0,0 +1,159 @@ +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { createMockCache } from "@tests/utils/mockCache"; +import { createMockLogger } from "@tests/utils/mockLogger"; +import { DeletionDetector } from "./DeletionDetector"; +import type { FileInfo } from "./types"; + +const mockCacheManager = createMockCache(); +const mockLogger = createMockLogger(); + +describe("DeletionDetector", () => { + let deletionDetector: DeletionDetector; + + beforeEach(() => { + deletionDetector = new DeletionDetector(mockCacheManager, mockLogger); + jest.clearAllMocks(); + }); + + describe("detectDeletions", () => { + it("should detect deletions when cache has extra keys", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(), + }, + ], + ]); + + mockCacheManager.keys.mockReturnValue([ + "fileA", + "block:block1", + "model:model1", + ]); + mockCacheManager.get.mockImplementation((key) => + key === "block:block1" + ? { id: "dummy-id", hash: "dummy-hash" } + : key === "model:model1" + ? { id: "dummy-id", hash: "dummy-hash" } + : undefined, + ); + + const summary = deletionDetector.detectDeletions(fileMap); + + expect(summary.total).toBe(2); + expect(summary.blocks).toHaveLength(1); + expect(summary.models).toHaveLength(1); + }); + + it("should return empty summary when no deletions found", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(), + }, + ], + ]); + + mockCacheManager.keys.mockReturnValue(["fileA"]); + + const summary = deletionDetector.detectDeletions(fileMap); + + expect(summary.total).toBe(0); + }); + }); + + describe("canSafelyDelete", () => { + it("should detect safe deletions", () => { + const fileMap = new Map(); + const candidate = { + key: "block:block1", + type: "block" as const, + name: "block1", + id: "dummy-id", + hash: "dummy-hash", + }; + + const result = deletionDetector.canSafelyDelete(candidate, fileMap); + + expect(result.canDelete).toBe(true); + expect(result.usedBy).toEqual([]); + }); + + it("should detect unsafe deletions", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["block:block1"]), + }, + ], + ]); + const candidate = { + key: "block:block1", + type: "block" as const, + name: "block1", + id: "dummy-id", + hash: "dummy-hash", + }; + + const result = deletionDetector.canSafelyDelete(candidate, fileMap); + + expect(result.canDelete).toBe(false); + expect(result.usedBy).toEqual(["fileA"]); + }); + }); + + describe("filterSafeDeletions", () => { + it("should filter safe and unsafe deletions", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["block:block1"]), + }, + ], + ]); + const candidates = [ + { + key: "block:block1", + type: "block" as const, + name: "block1", + id: "dummy-id", + hash: "dummy-hash", + }, + { + key: "model:model1", + type: "model" as const, + name: "model1", + id: "dummy-id", + hash: "dummy-hash", + }, + ]; + + const { safe, unsafe } = deletionDetector.filterSafeDeletions( + candidates, + fileMap, + ); + + expect(safe).toHaveLength(1); + expect(safe[0]?.name).toBe("model1"); + expect(unsafe).toHaveLength(1); + expect(unsafe[0]?.name).toBe("block1"); + expect(unsafe[0]?.usedBy).toEqual(["fileA"]); + }); + }); +}); diff --git a/src/commands/run/DeletionDetector.ts b/src/commands/run/DeletionDetector.ts new file mode 100644 index 0000000..038ce9f --- /dev/null +++ b/src/commands/run/DeletionDetector.ts @@ -0,0 +1,169 @@ +import type { CacheManager } from "@/cache/CacheManager"; +import type { ConsoleLogger } from "@/logger"; +import type { FileInfo } from "./types"; + +export interface DeletionCandidate { + key: string; + type: "block" | "model"; + name: string; + id: string; + hash: string; +} + +export interface DeletionSummary { + blocks: DeletionCandidate[]; + models: DeletionCandidate[]; + total: number; +} + +export class DeletionDetector { + constructor( + private readonly cache: CacheManager, + private readonly logger: ConsoleLogger, + ) { + this.logger.trace("Initializing DeletionDetector"); + } + + /** + * Detect items that are in the cache but not on the filesystem. + */ + public detectDeletions(fileMap: Map): DeletionSummary { + this.logger.trace("Starting deletion detection"); + + const cachedKeys = new Set(this.cache.keys()); + const currentFileKeys = new Set(fileMap.keys()); + + this.logger.traceJson("Deletion detection data", { + cachedKeysCount: cachedKeys.size, + currentFileKeysCount: currentFileKeys.size, + cachedKeys: Array.from(cachedKeys), + currentFileKeys: Array.from(currentFileKeys), + }); + + const deletionCandidates: DeletionCandidate[] = []; + + for (const cacheKey of cachedKeys) { + if (!currentFileKeys.has(cacheKey)) { + const cachedData = this.cache.get(cacheKey); + if (cachedData) { + const [type, name] = this.parseCacheKey(cacheKey); + if (type && name) { + deletionCandidates.push({ + key: cacheKey, + type, + name, + id: cachedData.id, + hash: cachedData.hash, + }); + + this.logger.traceJson("Found deletion candidate", { + key: cacheKey, + type, + name, + id: cachedData.id, + }); + } + } + } + } + + const blocks = deletionCandidates.filter((item) => item.type === "block"); + const models = deletionCandidates.filter((item) => item.type === "model"); + + const summary: DeletionSummary = { + blocks, + models, + total: deletionCandidates.length, + }; + + this.logger.traceJson("Deletion detection completed", { + totalCandidates: summary.total, + blocksCount: blocks.length, + modelsCount: models.length, + }); + + return summary; + } + + public canSafelyDelete( + candidate: DeletionCandidate, + fileMap: Map, + ): { canDelete: boolean; usedBy: string[] } { + this.logger.traceJson("Checking if item can be safely deleted", { + candidate: candidate.key, + type: candidate.type, + name: candidate.name, + }); + + const usedBy: string[] = []; + + for (const [fileKey, fileInfo] of fileMap.entries()) { + if (fileInfo.dependencies.has(candidate.key)) { + usedBy.push(fileKey); + } + } + + const canDelete = usedBy.length === 0; + + this.logger.traceJson("Safety check completed", { + candidate: candidate.key, + canDelete, + usedBy, + }); + + return { canDelete, usedBy }; + } + + public filterSafeDeletions( + candidates: DeletionCandidate[], + fileMap: Map, + ): { + safe: DeletionCandidate[]; + unsafe: Array; + } { + this.logger.trace("Filtering safe deletions"); + + const safe: DeletionCandidate[] = []; + const unsafe: Array = []; + + for (const candidate of candidates) { + const { canDelete, usedBy } = this.canSafelyDelete(candidate, fileMap); + + if (canDelete) { + safe.push(candidate); + } else { + unsafe.push({ ...candidate, usedBy }); + } + } + + this.logger.traceJson("Safe deletion filtering completed", { + totalCandidates: candidates.length, + safeCount: safe.length, + unsafeCount: unsafe.length, + }); + + return { safe, unsafe }; + } + + private parseCacheKey( + key: string, + ): [type: "block" | "model" | null, name: string | null] { + const colonIndex = key.indexOf(":"); + if (colonIndex === -1) { + this.logger.warn(`Invalid cache key format: ${key}`); + return [null, null]; + } + + const type = key.substring(0, colonIndex); + const name = key.substring(colonIndex + 1); + + if (type !== "block" && type !== "model") { + if (type !== "sync") { + this.logger.warn(`Unknown item type in cache key: ${type}`); + } + return [null, null]; + } + + return [type, name]; + } +} diff --git a/src/commands/run/DeletionManager.test.ts b/src/commands/run/DeletionManager.test.ts new file mode 100644 index 0000000..05c5257 --- /dev/null +++ b/src/commands/run/DeletionManager.test.ts @@ -0,0 +1,341 @@ +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { createMockCache } from "@tests/utils/mockCache"; +import { createMockLogger } from "@tests/utils/mockLogger"; +import type DatoApi from "../../Api/DatoApi"; +import type { DeletionCandidate, DeletionSummary } from "./DeletionDetector"; +import { DeletionManager } from "./DeletionManager"; + +// Mock inquirer +jest.mock("inquirer", () => ({ + prompt: jest.fn(), +})); + +// Mock dependencies +const mockApi = { + call: jest.fn(), + client: { + itemTypes: { + destroy: jest.fn(), + }, + }, +} as unknown as jest.Mocked; + +const mockCacheManager = createMockCache(); +const mockLogger = createMockLogger(); + +describe("DeletionManager", () => { + let deletionManager: DeletionManager; + + beforeEach(() => { + deletionManager = new DeletionManager( + mockApi, + mockCacheManager, + mockLogger, + ); + jest.clearAllMocks(); + }); + + describe("displayDeletionSummary", () => { + it("should display message when no items to delete", () => { + const summary: DeletionSummary = { + blocks: [], + models: [], + total: 0, + }; + + deletionManager.displayDeletionSummary(summary); + + expect(mockLogger.info).toHaveBeenCalledWith( + "No items to delete - all files match the cache.", + ); + }); + + it("should display deletion summary with blocks and models", () => { + const summary: DeletionSummary = { + blocks: [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + }, + ], + models: [ + { + key: "model:model1", + type: "model", + name: "model1", + id: "id2", + hash: "hash2", + }, + ], + total: 2, + }; + + deletionManager.displayDeletionSummary(summary); + + expect(mockLogger.info).toHaveBeenCalledWith( + "\n Found 2 item(s) to delete from DatoCMS:", + ); + expect(mockLogger.info).toHaveBeenCalledWith("\n Blocks (1):"); + expect(mockLogger.info).toHaveBeenCalledWith(" - block1 (id1)"); + expect(mockLogger.info).toHaveBeenCalledWith("\n Models (1):"); + expect(mockLogger.info).toHaveBeenCalledWith(" - model1 (id2)"); + }); + }); + + describe("displayUnsafeDeletions", () => { + it("should not display anything when no unsafe deletions", () => { + const unsafe: Array = []; + + deletionManager.displayUnsafeDeletions(unsafe); + + expect(mockLogger.warn).not.toHaveBeenCalled(); + }); + + it("should display unsafe deletion warnings", () => { + const unsafe: Array = [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + usedBy: ["fileA", "fileB"], + }, + ]; + + deletionManager.displayUnsafeDeletions(unsafe); + + expect(mockLogger.warn).toHaveBeenCalledWith( + "\n Warning: 1 item(s) cannot be safely deleted as they are still referenced:", + ); + expect(mockLogger.warn).toHaveBeenCalledWith(" - block: block1"); + expect(mockLogger.warn).toHaveBeenCalledWith(" Used by: fileA, fileB"); + }); + }); + + describe("confirmDeletions", () => { + it("should skip confirmation when skipConfirmation is true", async () => { + const safe: DeletionCandidate[] = [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + }, + ]; + + const result = await deletionManager.confirmDeletions(safe, { + skipConfirmation: true, + }); + + expect(result).toBe(safe); + }); + + it("should return empty array when no safe deletions", async () => { + const safe: DeletionCandidate[] = []; + + const result = await deletionManager.confirmDeletions(safe); + + expect(result).toEqual([]); + }); + + it("should prompt for confirmation when not skipping", async () => { + const inquirer = require("inquirer"); + const safe: DeletionCandidate[] = [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + }, + ]; + + inquirer.prompt.mockResolvedValueOnce({ + confirmedItems: safe, + }); + + const result = await deletionManager.confirmDeletions(safe); + + expect(result).toBe(safe); + expect(inquirer.prompt).toHaveBeenCalledWith([ + { + type: "checkbox", + name: "confirmedItems", + message: "Select items to delete from DatoCMS:", + choices: [ + { + name: "block: block1", + value: safe[0], + checked: true, + }, + ], + }, + ]); + }); + }); + + describe("deleteCandidates", () => { + it("should return empty array when no candidates", async () => { + const result = await deletionManager.deleteCandidates([]); + + expect(result).toEqual([]); + }); + + it("should successfully delete candidates", async () => { + const candidates: DeletionCandidate[] = [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + }, + ]; + + mockApi.call.mockResolvedValueOnce(undefined); + mockCacheManager.delete.mockResolvedValueOnce(true); + + const results = await deletionManager.deleteCandidates(candidates); + + expect(results).toEqual([ + { + success: true, + candidate: candidates[0], + }, + ]); + + expect(mockApi.call).toHaveBeenCalledWith( + expect.any(Function), + 3, + 500, + "itemTypes.destroy(id1, block1)", + ); + expect(mockCacheManager.delete).toHaveBeenCalledWith("block:block1"); + }); + + it("should handle deletion failures", async () => { + const candidates: DeletionCandidate[] = [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + }, + ]; + + const error = new Error("Deletion failed"); + mockApi.call.mockRejectedValueOnce(error); + + const results = await deletionManager.deleteCandidates(candidates); + + expect(results).toEqual([ + { + success: false, + candidate: candidates[0], + error, + }, + ]); + }); + }); + + describe("handleDeletions", () => { + it("should handle complete deletion flow", async () => { + const summary: DeletionSummary = { + blocks: [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + }, + ], + models: [], + total: 1, + }; + + const safe: DeletionCandidate[] = [summary.blocks[0]!]; + const unsafe: Array = []; + + const inquirer = require("inquirer"); + inquirer.prompt.mockResolvedValueOnce({ + confirmedItems: safe, + }); + + mockApi.call.mockResolvedValueOnce(undefined); + mockCacheManager.delete.mockResolvedValueOnce(true); + + const results = await deletionManager.handleDeletions( + summary, + safe, + unsafe, + ); + + expect(results).toHaveLength(1); + expect(results[0]?.success).toBe(true); + }); + + it("should handle case when no safe deletions", async () => { + const summary: DeletionSummary = { + blocks: [], + models: [], + total: 0, + }; + + const safe: DeletionCandidate[] = []; + const unsafe: Array = []; + + const results = await deletionManager.handleDeletions( + summary, + safe, + unsafe, + ); + + expect(results).toEqual([]); + expect(mockLogger.info).toHaveBeenCalledWith( + "No items can be safely deleted at this time.", + ); + }); + + it("should handle case when no deletions confirmed", async () => { + const summary: DeletionSummary = { + blocks: [ + { + key: "block:block1", + type: "block", + name: "block1", + id: "id1", + hash: "hash1", + }, + ], + models: [], + total: 1, + }; + + const safe: DeletionCandidate[] = [summary.blocks[0]!]; + const unsafe: Array = []; + + const inquirer = require("inquirer"); + inquirer.prompt.mockResolvedValueOnce({ + confirmedItems: [], + }); + + const results = await deletionManager.handleDeletions( + summary, + safe, + unsafe, + ); + + expect(results).toEqual([]); + expect(mockLogger.info).toHaveBeenCalledWith( + "No deletions confirmed. Skipping deletion process.", + ); + }); + }); +}); diff --git a/src/commands/run/DeletionManager.ts b/src/commands/run/DeletionManager.ts new file mode 100644 index 0000000..f8dc600 --- /dev/null +++ b/src/commands/run/DeletionManager.ts @@ -0,0 +1,190 @@ +import inquirer from "inquirer"; +import type DatoApi from "../../Api/DatoApi"; +import type { CacheManager } from "../../cache/CacheManager"; +import type { ConsoleLogger } from "../../logger"; +import type { DeletionCandidate, DeletionSummary } from "./DeletionDetector"; + +export interface DeletionResult { + success: boolean; + candidate: DeletionCandidate; + error?: Error; +} + +export interface DeletionOptions { + confirmEach?: boolean; + confirmAll?: boolean; + skipConfirmation?: boolean; +} + +export class DeletionManager { + constructor( + private readonly api: DatoApi, + private readonly cache: CacheManager, + private readonly logger: ConsoleLogger, + ) { + this.logger.trace("Initializing DeletionManager"); + } + + public displayDeletionSummary(summary: DeletionSummary): void { + if (summary.total === 0) { + this.logger.info("No items to delete - all files match the cache."); + return; + } + + this.logger.info( + `\n Found ${summary.total} item(s) to delete from DatoCMS:`, + ); + + if (summary.blocks.length > 0) { + this.logger.info(`\n Blocks (${summary.blocks.length}):`); + for (const block of summary.blocks) { + this.logger.info(` - ${block.name} (${block.id})`); + } + } + + if (summary.models.length > 0) { + this.logger.info(`\n Models (${summary.models.length}):`); + for (const model of summary.models) { + this.logger.info(` - ${model.name} (${model.id})`); + } + } + + this.logger.info( + "\n These items exist in DatoCMS but their corresponding files have been deleted locally.", + ); + } + + public displayUnsafeDeletions( + unsafe: Array, + ): void { + if (unsafe.length === 0) { + return; + } + + this.logger.warn( + `\n Warning: ${unsafe.length} item(s) cannot be safely deleted as they are still referenced:`, + ); + + for (const item of unsafe) { + this.logger.warn(` - ${item.type}: ${item.name}`); + this.logger.warn(` Used by: ${item.usedBy.join(", ")}`); + } + + this.logger.warn( + "\nTo delete these items, first remove their dependencies or delete the files that reference them.", + ); + } + + public async confirmDeletions( + safe: DeletionCandidate[], + options: DeletionOptions = {}, + ): Promise { + if (options.skipConfirmation) { + return safe; + } + + if (safe.length === 0) { + return []; + } + + const choices = safe.map((candidate) => ({ + name: `${candidate.type}: ${candidate.name}`, + value: candidate, + checked: true, + })); + + const { confirmedItems } = await inquirer.prompt([ + { + type: "checkbox", + name: "confirmedItems", + message: "Select items to delete from DatoCMS:", + choices, + }, + ]); + + return confirmedItems; + } + + public async deleteCandidates( + candidates: DeletionCandidate[], + ): Promise { + if (candidates.length === 0) { + return []; + } + + this.logger.info( + `\n️ Deleting ${candidates.length} item(s) from DatoCMS...`, + ); + + const results: DeletionResult[] = []; + + for (const candidate of candidates) { + try { + this.logger.info(`Deleting ${candidate.type}: ${candidate.name}`); + + await this.api.call( + () => this.api.client.itemTypes.destroy(candidate.id), + 3, + 500, + `itemTypes.destroy(${candidate.id}, ${candidate.name})` + ); + + await this.cache.delete(candidate.key); + + results.push({ + success: true, + candidate, + }); + + this.logger.success(`Deleted ${candidate.type}: ${candidate.name}`); + } catch (error) { + const err = error as Error; + results.push({ + success: false, + candidate, + error: err, + }); + + this.logger.error( + `Failed to delete ${candidate.type}: ${candidate.name} - ${err.message}`, + ); + } + } + + const successful = results.filter((r) => r.success).length; + const failed = results.filter((r) => !r.success).length; + + this.logger.info( + `\n Deletion summary: ${successful} successful, ${failed} failed`, + ); + + return results; + } + + public async handleDeletions( + summary: DeletionSummary, + safe: DeletionCandidate[], + unsafe: Array, + options: DeletionOptions = {}, + ): Promise { + this.logger.trace("Starting deletion handling process"); + + this.displayDeletionSummary(summary); + + this.displayUnsafeDeletions(unsafe); + + if (safe.length === 0) { + this.logger.info("No items can be safely deleted at this time."); + return []; + } + + const confirmedDeletions = await this.confirmDeletions(safe, options); + + if (confirmedDeletions.length === 0) { + this.logger.info("No deletions confirmed. Skipping deletion process."); + return []; + } + + return await this.deleteCandidates(confirmedDeletions); + } +} diff --git a/src/commands/run/DependencyAnalyzer.ts b/src/commands/run/DependencyAnalyzer.ts new file mode 100644 index 0000000..fa65874 --- /dev/null +++ b/src/commands/run/DependencyAnalyzer.ts @@ -0,0 +1,139 @@ +import type { ConsoleLogger } from "../../logger"; +import type { BuilderContext } from "../../types/BuilderContext"; +import type { DatoBuilderConfig } from "../../types/DatoBuilderConfig"; +import type { FileInfo } from "./types"; + +export class DependencyAnalyzer { + constructor( + private readonly config: Required, + private readonly logger: ConsoleLogger, + ) { + this.logger.traceJson("Initializing DependencyAnalyzer", { + config: { + logLevel: config.logLevel, + apiToken: config.apiToken ? "***" : "undefined", + }, + }); + } + + async analyzeDependencies(fileMap: Map): Promise { + this.logger.traceJson("Starting dependency analysis", { + fileCount: fileMap.size, + }); + this.logger.debug("Analyzing dependencies..."); + + this.logger.trace("Creating analysis promises for all files"); + const analysisPromises = Array.from(fileMap.entries()).map( + async ([fileKey, fileInfo]) => { + this.logger.traceJson("Analyzing dependencies for file", { + fileKey, + type: fileInfo.type, + name: fileInfo.name, + filePath: fileInfo.filePath, + }); + + const contextLogger = this.logger.child({ + [fileInfo.type]: fileInfo.name, + operation: "analyze-deps", + }); + + try { + this.logger.traceJson("Extracting dependencies from file", { + fileKey, + }); + const dependencies = await this.extractDependencies(fileInfo); + fileInfo.dependencies = dependencies; + + this.logger.traceJson("Dependencies extracted", { + fileKey, + dependencyCount: dependencies.size, + dependencies: Array.from(dependencies), + }); + + if (dependencies.size > 0) { + contextLogger.debug( + `Dependencies found: ${Array.from(dependencies).join(", ")}`, + ); + } else { + contextLogger.debug("No dependencies found"); + } + } catch (error: unknown) { + this.logger.errorJson("Dependency analysis failed", { + fileKey, + error: (error as Error).message, + stack: (error as Error).stack, + }); + contextLogger.warn( + `Failed to analyze dependencies: ${(error as Error).message}`, + ); + fileInfo.dependencies = new Set(); + } + }, + ); + + this.logger.trace("Executing all dependency analysis promises"); + await Promise.all(analysisPromises); + this.logger.trace("Dependency analysis completed"); + } + + private async extractDependencies(fileInfo: FileInfo): Promise> { + this.logger.traceJson("Extracting dependencies", { + type: fileInfo.type, + name: fileInfo.name, + filePath: fileInfo.filePath, + }); + + const dependencies = new Set(); + const proxyContext = this.createProxyContext(dependencies); + + this.logger.traceJson("Loading module for dependency extraction", { + filePath: fileInfo.filePath, + }); + const moduleExports = await import(fileInfo.filePath); + const buildFunction = moduleExports.default; + + if (typeof buildFunction !== "function") { + this.logger.errorJson( + "Module does not export default function for dependency extraction", + { filePath: fileInfo.filePath }, + ); + throw new Error( + `${fileInfo.type} "${fileInfo.name}" does not export a default function`, + ); + } + + this.logger.trace( + "Executing build function with proxy context to extract dependencies", + ); + await buildFunction(proxyContext); + + this.logger.traceJson("Dependencies extracted successfully", { + type: fileInfo.type, + name: fileInfo.name, + dependencyCount: dependencies.size, + dependencies: Array.from(dependencies), + }); + + return dependencies; + } + + private createProxyContext(dependencies: Set): BuilderContext { + this.logger.trace("Creating proxy context for dependency extraction"); + + return { + config: this.config, + getBlock: (name: string) => { + const dependencyKey = `block:${name}`; + this.logger.traceJson("Proxy getBlock called", { name, dependencyKey }); + dependencies.add(dependencyKey); + return Promise.resolve("temp-id"); + }, + getModel: (name: string) => { + const dependencyKey = `model:${name}`; + this.logger.traceJson("Proxy getModel called", { name, dependencyKey }); + dependencies.add(dependencyKey); + return Promise.resolve("temp-id"); + }, + }; + } +} diff --git a/src/commands/run/DependencyResolver.test.ts b/src/commands/run/DependencyResolver.test.ts new file mode 100644 index 0000000..b7d043c --- /dev/null +++ b/src/commands/run/DependencyResolver.test.ts @@ -0,0 +1,401 @@ +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { createMockLogger } from "@tests/utils/mockLogger"; +import { DependencyResolver } from "./DependencyResolver"; +import type { FileInfo } from "./types"; + +const mockLogger = createMockLogger(); + +describe("DependencyResolver", () => { + let resolver: DependencyResolver; + + beforeEach(() => { + resolver = new DependencyResolver(mockLogger); + jest.clearAllMocks(); + }); + + describe("topologicalSort", () => { + it("should handle empty file map", () => { + const fileMap = new Map(); + const result = resolver.topologicalSort(fileMap); + expect(result).toEqual([]); + }); + + it("should handle single file with no dependencies", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(), + }, + ], + ]); + const result = resolver.topologicalSort(fileMap); + expect(result).toEqual(["fileA"]); + }); + + it("should handle multiple files with no dependencies", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(), + }, + ], + [ + "fileC", + { + name: "fileC", + type: "block", + filePath: "/path/to/fileC", + dependencies: new Set(), + }, + ], + ]); + const result = resolver.topologicalSort(fileMap); + expect(result).toHaveLength(3); + expect(result).toContain("fileA"); + expect(result).toContain("fileB"); + expect(result).toContain("fileC"); + }); + + it("should handle simple linear dependency chain", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(["fileC"]), + }, + ], + [ + "fileC", + { + name: "fileC", + type: "block", + filePath: "/path/to/fileC", + dependencies: new Set(), + }, + ], + ]); + const result = resolver.topologicalSort(fileMap); + expect(result).toEqual(["fileC", "fileB", "fileA"]); + }); + + it("should handle complex dependency graph", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB", "fileC"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(["fileD"]), + }, + ], + [ + "fileC", + { + name: "fileC", + type: "block", + filePath: "/path/to/fileC", + dependencies: new Set(["fileD"]), + }, + ], + [ + "fileD", + { + name: "fileD", + type: "model", + filePath: "/path/to/fileD", + dependencies: new Set(), + }, + ], + [ + "fileE", + { + name: "fileE", + type: "block", + filePath: "/path/to/fileE", + dependencies: new Set(), + }, + ], + ]); + const result = resolver.topologicalSort(fileMap); + + // Check that dependencies come before dependents + expect(result.indexOf("fileD")).toBeLessThan(result.indexOf("fileB")); + expect(result.indexOf("fileD")).toBeLessThan(result.indexOf("fileC")); + expect(result.indexOf("fileB")).toBeLessThan(result.indexOf("fileA")); + expect(result.indexOf("fileC")).toBeLessThan(result.indexOf("fileA")); + expect(result).toContain("fileE"); + expect(result).toHaveLength(5); + }); + + it("should handle files with dependencies not in the file map", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB", "externalDep"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(), + }, + ], + ]); + const result = resolver.topologicalSort(fileMap); + expect(result).toEqual(["fileB", "fileA"]); + }); + + it("should detect circular dependencies - simple cycle", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(["fileA"]), + }, + ], + ]); + + expect(() => resolver.topologicalSort(fileMap)).toThrow( + "Circular dependency detected involving: fileA", + ); + }); + + it("should detect circular dependencies - complex cycle", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(["fileC"]), + }, + ], + [ + "fileC", + { + name: "fileC", + type: "block", + filePath: "/path/to/fileC", + dependencies: new Set(["fileA"]), + }, + ], + ]); + + expect(() => resolver.topologicalSort(fileMap)).toThrow( + "Circular dependency detected involving: fileA", + ); + }); + + it("should detect self-referencing dependency", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileA"]), + }, + ], + ]); + + expect(() => resolver.topologicalSort(fileMap)).toThrow( + "Circular dependency detected involving: fileA", + ); + }); + + it("should handle mixed scenario with cycles and non-cycles", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(["fileC"]), + }, + ], + [ + "fileC", + { + name: "fileC", + type: "block", + filePath: "/path/to/fileC", + dependencies: new Set(["fileA"]), + }, + ], + [ + "fileD", + { + name: "fileD", + type: "model", + filePath: "/path/to/fileD", + dependencies: new Set(), + }, + ], + ]); + + expect(() => resolver.topologicalSort(fileMap)).toThrow( + "Circular dependency detected involving: fileA", + ); + }); + + it("should handle diamond dependency pattern", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB", "fileC"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(["fileD"]), + }, + ], + [ + "fileC", + { + name: "fileC", + type: "block", + filePath: "/path/to/fileC", + dependencies: new Set(["fileD"]), + }, + ], + [ + "fileD", + { + name: "fileD", + type: "model", + filePath: "/path/to/fileD", + dependencies: new Set(), + }, + ], + ]); + + const result = resolver.topologicalSort(fileMap); + + // fileD should come first + expect(result.indexOf("fileD")).toBe(0); + // fileB and fileC should come after fileD but before fileA + expect(result.indexOf("fileB")).toBeGreaterThan(result.indexOf("fileD")); + expect(result.indexOf("fileC")).toBeGreaterThan(result.indexOf("fileD")); + expect(result.indexOf("fileA")).toBeGreaterThan(result.indexOf("fileB")); + expect(result.indexOf("fileA")).toBeGreaterThan(result.indexOf("fileC")); + expect(result).toHaveLength(4); + }); + + it("should handle files with missing file info", () => { + const fileMap = new Map([ + [ + "fileA", + { + name: "fileA", + type: "block", + filePath: "/path/to/fileA", + dependencies: new Set(["fileB"]), + }, + ], + [ + "fileB", + { + name: "fileB", + type: "model", + filePath: "/path/to/fileB", + dependencies: new Set(), + }, + ], + ]); + // Delete fileB's info to simulate missing file info + fileMap.delete("fileB"); + + const result = resolver.topologicalSort(fileMap); + expect(result).toContain("fileA"); + expect(result).not.toContain("fileB"); // fileB should not be in result since it's missing + }); + }); +}); diff --git a/src/commands/run/DependencyResolver.ts b/src/commands/run/DependencyResolver.ts new file mode 100644 index 0000000..11c5b04 --- /dev/null +++ b/src/commands/run/DependencyResolver.ts @@ -0,0 +1,99 @@ +import type { ConsoleLogger } from "@/logger"; +import type { FileInfo } from "./types"; + +export class DependencyResolver { + constructor(private readonly logger: ConsoleLogger) { + this.logger.trace("Initializing DependencyResolver"); + } + + topologicalSort(fileMap: Map): string[] { + this.logger.traceJson("Starting topological sort", { + fileCount: fileMap.size, + }); + + const visited = new Set(); + const visiting = new Set(); + const result: string[] = []; + + const visit = (fileKey: string) => { + this.logger.traceJson("Visiting node", { + fileKey, + visited: visited.size, + visiting: visiting.size, + }); + + if (visiting.has(fileKey)) { + this.logger.errorJson("Circular dependency detected", { + fileKey, + visiting: Array.from(visiting), + }); + throw new Error(`Circular dependency detected involving: ${fileKey}`); + } + + if (visited.has(fileKey)) { + this.logger.traceJson("Node already visited, skipping", { fileKey }); + return; + } + + this.logger.traceJson("Adding node to visiting set", { fileKey }); + visiting.add(fileKey); + + const fileInfo = fileMap.get(fileKey); + if (fileInfo) { + this.logger.traceJson("Processing dependencies", { + fileKey, + dependencyCount: fileInfo.dependencies.size, + dependencies: Array.from(fileInfo.dependencies), + }); + + for (const dep of Array.from(fileInfo.dependencies)) { + if (fileMap.has(dep)) { + this.logger.traceJson("Visiting dependency", { + fileKey, + dependency: dep, + }); + visit(dep); + } else { + this.logger.warn(`Dependency "${dep}" not found for ${fileKey}`); + this.logger.traceJson("Dependency not found in file map", { + fileKey, + dependency: dep, + }); + } + } + } else { + this.logger.traceJson("File info not found for key", { fileKey }); + } + + this.logger.traceJson( + "Removing node from visiting set and adding to visited", + { fileKey }, + ); + visiting.delete(fileKey); + visited.add(fileKey); + result.push(fileKey); + this.logger.traceJson("Node added to result", { + fileKey, + resultLength: result.length, + }); + }; + + this.logger.trace("Starting traversal of all file keys"); + for (const fileKey of Array.from(fileMap.keys())) { + if (!visited.has(fileKey)) { + this.logger.traceJson("Starting visit for unvisited node", { fileKey }); + visit(fileKey); + } else { + this.logger.traceJson("Skipping already visited node", { fileKey }); + } + } + + this.logger.traceJson("Topological sort completed", { + totalNodes: fileMap.size, + resultLength: result.length, + result: result, + }); + + return result; + } +} diff --git a/src/commands/run/FileDiscoverer.test.ts b/src/commands/run/FileDiscoverer.test.ts new file mode 100644 index 0000000..36621b7 --- /dev/null +++ b/src/commands/run/FileDiscoverer.test.ts @@ -0,0 +1,200 @@ +import path from "node:path"; +import { + afterEach, + beforeEach, + describe, + expect, + it, + jest, +} from "@jest/globals"; +import { createMockLogger } from "@tests/utils/mockLogger"; +import { glob } from "glob"; +import { FileDiscoverer } from "./FileDiscoverer"; + +// Mock the glob module +jest.mock("glob", () => ({ + glob: jest.fn(), +})); + +// Mock the path module +jest.mock("node:path", () => ({ + basename: jest.fn(), + extname: jest.fn(), + resolve: jest.fn(), +})); + +describe("FileDiscoverer", () => { + let fileDiscoverer: FileDiscoverer; + let mockGlob: jest.MockedFunction; + let mockPath: jest.Mocked; + + const blocksPath = "/test/blocks"; + const modelsPath = "/test/models"; + + beforeEach(() => { + // Setup mocks + mockGlob = glob as jest.MockedFunction; + mockPath = path as jest.Mocked; + + // Create FileDiscoverer instance + fileDiscoverer = new FileDiscoverer( + blocksPath, + modelsPath, + createMockLogger(), + ); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("discoverFiles", () => { + it("should discover both block and model files successfully", async () => { + // Mock file paths + const blockFiles = ["/test/blocks/block1.ts", "/test/blocks/block2.js"]; + const modelFiles = ["/test/models/model1.ts", "/test/models/model2.js"]; + + // Mock glob responses + mockGlob + .mockResolvedValueOnce(blockFiles) + .mockResolvedValueOnce(modelFiles); + + // Mock path operations + mockPath.basename + .mockReturnValueOnce("block1") + .mockReturnValueOnce("block2") + .mockReturnValueOnce("model1") + .mockReturnValueOnce("model2"); + + mockPath.extname + .mockReturnValueOnce(".ts") + .mockReturnValueOnce(".js") + .mockReturnValueOnce(".ts") + .mockReturnValueOnce(".js"); + + mockPath.resolve + .mockReturnValueOnce("/resolved/blocks/block1.ts") + .mockReturnValueOnce("/resolved/blocks/block2.js") + .mockReturnValueOnce("/resolved/models/model1.ts") + .mockReturnValueOnce("/resolved/models/model2.js"); + + const result = await fileDiscoverer.discoverFiles(); + + // Verify glob calls + expect(mockGlob).toHaveBeenCalledTimes(2); + expect(mockGlob).toHaveBeenCalledWith(`${blocksPath}/**/*.{ts,js}`); + expect(mockGlob).toHaveBeenCalledWith(`${modelsPath}/**/*.{ts,js}`); + + // Verify result + expect(result.size).toBe(4); + expect(result.has("block:block1")).toBe(true); + expect(result.has("block:block2")).toBe(true); + expect(result.has("model:model1")).toBe(true); + expect(result.has("model:model2")).toBe(true); + + // Verify file info structure + const blockInfo = result.get("block:block1"); + expect(blockInfo).toEqual({ + name: "block1", + type: "block", + filePath: "/resolved/blocks/block1.ts", + dependencies: new Set(), + }); + + const modelInfo = result.get("model:model1"); + expect(modelInfo).toEqual({ + name: "model1", + type: "model", + filePath: "/resolved/models/model1.ts", + dependencies: new Set(), + }); + }); + + it("should handle empty block directory", async () => { + const blockFiles: string[] = []; + const modelFiles = ["/test/models/model1.ts"]; + + mockGlob + .mockResolvedValueOnce(blockFiles) + .mockResolvedValueOnce(modelFiles); + + mockPath.basename.mockReturnValueOnce("model1"); + mockPath.extname.mockReturnValueOnce(".ts"); + mockPath.resolve.mockReturnValueOnce("/resolved/models/model1.ts"); + + const result = await fileDiscoverer.discoverFiles(); + + expect(result.size).toBe(1); + expect(result.has("model:model1")).toBe(true); + }); + + it("should handle empty model directory", async () => { + const blockFiles = ["/test/blocks/block1.ts"]; + const modelFiles: string[] = []; + + mockGlob + .mockResolvedValueOnce(blockFiles) + .mockResolvedValueOnce(modelFiles); + + mockPath.basename.mockReturnValueOnce("block1"); + mockPath.extname.mockReturnValueOnce(".ts"); + mockPath.resolve.mockReturnValueOnce("/resolved/blocks/block1.ts"); + + const result = await fileDiscoverer.discoverFiles(); + + expect(result.size).toBe(1); + expect(result.has("block:block1")).toBe(true); + }); + + it("should handle no files found in either directory", async () => { + const blockFiles: string[] = []; + const modelFiles: string[] = []; + + mockGlob + .mockResolvedValueOnce(blockFiles) + .mockResolvedValueOnce(modelFiles); + + const result = await fileDiscoverer.discoverFiles(); + + expect(result.size).toBe(0); + }); + + it("should handle glob errors gracefully", async () => { + const error = new Error("Glob error"); + mockGlob.mockRejectedValueOnce(error); + + await expect(fileDiscoverer.discoverFiles()).rejects.toThrow( + "Glob error", + ); + }); + + it("should handle files with different extensions", async () => { + const blockFiles = ["/test/blocks/block1.ts", "/test/blocks/block2.js"]; + const modelFiles: string[] = []; + + mockGlob + .mockResolvedValueOnce(blockFiles) + .mockResolvedValueOnce(modelFiles); + + mockPath.basename + .mockReturnValueOnce("block1") + .mockReturnValueOnce("block2"); + mockPath.extname.mockReturnValueOnce(".ts").mockReturnValueOnce(".js"); + mockPath.resolve + .mockReturnValueOnce("/resolved/blocks/block1.ts") + .mockReturnValueOnce("/resolved/blocks/block2.js"); + + const result = await fileDiscoverer.discoverFiles(); + + expect(result.size).toBe(2); + expect(result.has("block:block1")).toBe(true); + expect(result.has("block:block2")).toBe(true); + + const block1Info = result.get("block:block1"); + expect(block1Info?.filePath).toBe("/resolved/blocks/block1.ts"); + + const block2Info = result.get("block:block2"); + expect(block2Info?.filePath).toBe("/resolved/blocks/block2.js"); + }); + }); +}); diff --git a/src/commands/run/FileDiscoverer.ts b/src/commands/run/FileDiscoverer.ts new file mode 100644 index 0000000..54b1943 --- /dev/null +++ b/src/commands/run/FileDiscoverer.ts @@ -0,0 +1,122 @@ +import path from "node:path"; +import { glob } from "glob"; +import type { ConsoleLogger } from "@/logger"; +import type { FileInfo } from "./types"; + +export class FileDiscoverer { + constructor( + private readonly blocksPath: string, + private readonly modelsPath: string, + private readonly logger: ConsoleLogger, + ) { + this.logger.traceJson("Initializing FileDiscoverer", { + blocksPath: this.blocksPath, + modelsPath: this.modelsPath, + }); + } + + async discoverFiles(): Promise> { + this.logger.trace("Starting file discovery"); + + this.logger.trace("Running glob patterns for blocks and models"); + const [blockFiles, modelFiles] = await Promise.all([ + glob(`${this.blocksPath}/**/*.{ts,js}`), + glob(`${this.modelsPath}/**/*.{ts,js}`), + ]); + + this.logger.traceJson("Glob patterns completed", { + blockFilesCount: blockFiles.length, + modelFilesCount: modelFiles.length, + }); + + // Check for empty directories and log specific warnings + if (blockFiles.length === 0) { + this.logger.warn( + `No block files found in "${this.blocksPath}". Ensure you have .ts or .js files in the blocks directory.`, + ); + this.logger.traceJson("No block files discovered", { + searchPath: this.blocksPath, + pattern: `${this.blocksPath}/**/*.{ts,js}`, + }); + } + + if (modelFiles.length === 0) { + this.logger.warn( + `No model files found in "${this.modelsPath}". Ensure you have .ts or .js files in the models directory.`, + ); + this.logger.traceJson("No model files discovered", { + searchPath: this.modelsPath, + pattern: `${this.modelsPath}/**/*.{ts,js}`, + }); + } + + const fileMap = new Map(); + + this.logger.trace("Processing block files"); + for (const file of blockFiles) { + const name = path.basename(file, path.extname(file)); + const fileKey = `block:${name}`; + const resolvedPath = path.resolve(file); + + this.logger.traceJson("Adding block file to map", { + originalPath: file, + resolvedPath, + name, + fileKey, + }); + + fileMap.set(fileKey, { + name, + type: "block", + filePath: resolvedPath, + dependencies: new Set(), + }); + } + + this.logger.trace("Processing model files"); + for (const file of modelFiles) { + const name = path.basename(file, path.extname(file)); + const fileKey = `model:${name}`; + const resolvedPath = path.resolve(file); + + this.logger.traceJson("Adding model file to map", { + originalPath: file, + resolvedPath, + name, + fileKey, + }); + + fileMap.set(fileKey, { + name, + type: "model", + filePath: resolvedPath, + dependencies: new Set(), + }); + } + + this.logger.traceJson("File discovery completed", { + totalFiles: fileMap.size, + blockFiles: blockFiles.length, + modelFiles: modelFiles.length, + }); + + if (fileMap.size === 0) { + this.logger.warn( + `No blocks or models found. Please ensure you have files in "${this.blocksPath}" or "${this.modelsPath}".`, + ); + this.logger.trace("No files found during discovery"); + } else { + this.logger.debug( + `Discovered ${blockFiles.length} blocks and ${modelFiles.length} models`, + ); + this.logger.traceJson("File discovery summary", { + totalFiles: fileMap.size, + blockFiles: blockFiles.length, + modelFiles: modelFiles.length, + fileKeys: Array.from(fileMap.keys()), + }); + } + + return fileMap; + } +} diff --git a/src/commands/run/ItemBuilder.test.ts b/src/commands/run/ItemBuilder.test.ts new file mode 100644 index 0000000..3f50761 --- /dev/null +++ b/src/commands/run/ItemBuilder.test.ts @@ -0,0 +1,197 @@ +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { createMockCache } from "@tests/utils/mockCache"; +import { createMockConfig } from "@tests/utils/mockConfig"; +import { createMockLogger } from "@tests/utils/mockLogger"; +import type { BuilderContext } from "@/types/BuilderContext"; +import { ItemBuildError, ItemBuilder } from "./ItemBuilder"; +import type { FileInfo } from "./types"; + +// Mock CacheManager +const mockCacheManager = createMockCache(); +const mockLogger = createMockLogger(); +const mockContext: BuilderContext = { + config: createMockConfig(), + getBlock: jest.fn<(name: string) => Promise>((name: string) => + Promise.resolve(`block:${name}`), + ), + getModel: jest.fn<(name: string) => Promise>((name: string) => + Promise.resolve(`model:${name}`), + ), +}; + +const mockGetContext = jest + .fn<() => BuilderContext>() + .mockReturnValue(mockContext); + +// Mock builder instances +const mockBlockBuilder = { + upsert: jest.fn(), + getHash: jest.fn(), + constructor: { name: "BlockBuilder" }, +}; + +const mockModelBuilder = { + upsert: jest.fn(), + getHash: jest.fn(), + constructor: { name: "ModelBuilder" }, +}; + +// Mock dynamic imports +jest.mock( + "/path/to/block1.ts", + () => jest.fn().mockImplementation(() => Promise.resolve(mockBlockBuilder)), + { virtual: true }, +); +jest.mock( + "/path/to/model1.ts", + () => jest.fn().mockImplementation(() => Promise.resolve(mockModelBuilder)), + { virtual: true }, +); +jest.mock( + "/path/to/invalid.ts", + () => jest.fn().mockImplementation(() => Promise.resolve("not-a-builder")), + { virtual: true }, +); +jest.mock( + "/path/to/no-default.ts", + () => ({ + __esModule: true, + }), + { virtual: true }, +); + +describe("ItemBuilder", () => { + let itemBuilder: ItemBuilder; + + beforeEach(() => { + itemBuilder = new ItemBuilder(mockCacheManager, mockLogger, mockGetContext); + jest.clearAllMocks(); + }); + + describe("buildItem", () => { + it("should build item from cache when hash matches", async () => { + const fileInfo: FileInfo = { + name: "block1", + type: "block", + filePath: "/path/to/block1.ts", + dependencies: new Set(), + }; + + const cachedData = { id: "cached-id", hash: "cached-hash" }; + mockCacheManager.get.mockReturnValue(cachedData); + mockBlockBuilder.getHash.mockReturnValue("cached-hash"); + + const result = await itemBuilder.buildItem(fileInfo); + + expect(result).toEqual({ id: "cached-id", fromCache: true }); + expect(mockBlockBuilder.upsert).not.toHaveBeenCalled(); + }); + + it("should build item from source when not in cache", async () => { + const fileInfo: FileInfo = { + name: "block1", + type: "block", + filePath: "/path/to/block1.ts", + dependencies: new Set(), + }; + + mockCacheManager.get.mockReturnValue(undefined); + (mockBlockBuilder.upsert as any).mockResolvedValue("new-id"); + mockBlockBuilder.getHash.mockReturnValue("new-hash"); + + const result = await itemBuilder.buildItem(fileInfo); + + expect(result).toEqual({ id: "new-id", fromCache: false }); + expect(mockBlockBuilder.upsert).toHaveBeenCalled(); + expect(mockCacheManager.set).toHaveBeenCalledWith("block:block1", { + id: "new-id", + hash: "new-hash", + }); + }); + + it("should build item from source when hash mismatch", async () => { + const fileInfo: FileInfo = { + name: "block1", + type: "block", + filePath: "/path/to/block1.ts", + dependencies: new Set(), + }; + + const cachedData = { id: "cached-id", hash: "old-hash" }; + mockCacheManager.get.mockReturnValue(cachedData); + mockBlockBuilder.getHash.mockReturnValue("new-hash"); + (mockBlockBuilder.upsert as any).mockResolvedValue("new-id"); + + const result = await itemBuilder.buildItem(fileInfo); + + expect(result).toEqual({ id: "new-id", fromCache: false }); + expect(mockBlockBuilder.upsert).toHaveBeenCalled(); + }); + + it("should handle model building", async () => { + const fileInfo: FileInfo = { + name: "model1", + type: "model", + filePath: "/path/to/model1.ts", + dependencies: new Set(), + }; + + mockCacheManager.get.mockReturnValue(undefined); + (mockModelBuilder.upsert as any).mockResolvedValue("model-id"); + mockModelBuilder.getHash.mockReturnValue("model-hash"); + + const result = await itemBuilder.buildItem(fileInfo); + + expect(result).toEqual({ id: "model-id", fromCache: false }); + expect(mockModelBuilder.upsert).toHaveBeenCalled(); + }); + + it("should throw ItemBuildError when module does not export default function", async () => { + const fileInfo: FileInfo = { + name: "no-default", + type: "block", + filePath: "/path/to/no-default.ts", + dependencies: new Set(), + }; + + mockCacheManager.get.mockReturnValue(undefined); + + await expect(itemBuilder.buildItem(fileInfo)).rejects.toThrow( + ItemBuildError, + ); + }); + + it("should throw ItemBuildError when builder is invalid", async () => { + const fileInfo: FileInfo = { + name: "invalid", + type: "block", + filePath: "/path/to/invalid.ts", + dependencies: new Set(), + }; + + mockCacheManager.get.mockReturnValue(undefined); + + await expect(itemBuilder.buildItem(fileInfo)).rejects.toThrow( + ItemBuildError, + ); + }); + + it("should throw ItemBuildError when build fails", async () => { + const fileInfo: FileInfo = { + name: "block1", + type: "block", + filePath: "/path/to/block1.ts", + dependencies: new Set(), + }; + + mockCacheManager.get.mockReturnValue(undefined); + (mockBlockBuilder.upsert as any).mockRejectedValue( + new Error("Build failed"), + ); + + await expect(itemBuilder.buildItem(fileInfo)).rejects.toThrow( + ItemBuildError, + ); + }); + }); +}); diff --git a/src/commands/run/ItemBuilder.ts b/src/commands/run/ItemBuilder.ts new file mode 100644 index 0000000..49b3e5d --- /dev/null +++ b/src/commands/run/ItemBuilder.ts @@ -0,0 +1,362 @@ +import type BlockBuilder from "@/BlockBuilder"; +import type { CacheManager } from "@/cache/CacheManager"; +import type { ConsoleLogger } from "@/logger"; +import type ModelBuilder from "@/ModelBuilder"; +import type { BuilderContext } from "@/types/BuilderContext"; +import type { FileInfo } from "./types"; + +export class ItemBuildError extends Error { + constructor( + message: string, + public readonly itemType: string, + public readonly itemName: string, + public readonly cause?: Error, + ) { + super(message); + this.name = "ItemBuildError"; + } +} + +export class ItemBuilder { + // Cache for loaded modules to avoid repeated imports + private readonly moduleCache = new Map(); + + // Cache for computed hashes to avoid recomputation + private readonly hashCache = new Map(); + + constructor( + private readonly cache: CacheManager, + private readonly logger: ConsoleLogger, + private readonly getContext: () => BuilderContext, + ) { + this.logger.trace("Initializing ItemBuilder"); + } + + async buildItem( + fileInfo: FileInfo, + ): Promise<{ id: string; fromCache: boolean }> { + this.logger.traceJson("Starting item build", { + type: fileInfo.type, + name: fileInfo.name, + filePath: fileInfo.filePath, + }); + + const contextLogger = this.logger.child({ + [fileInfo.type]: fileInfo.name, + operation: "build", + }); + + try { + // Try to use cache first + this.logger.trace("Attempting to use cache"); + const cachedResult = await this.tryUseCache(fileInfo, contextLogger); + if (cachedResult) { + this.logger.traceJson("Using cached result", { id: cachedResult }); + return { id: cachedResult, fromCache: true }; + } + + // Build the item + this.logger.trace("Cache miss, building from source"); + const id = await this.buildFromSource(fileInfo, contextLogger); + this.logger.traceJson("Build completed successfully", { id }); + return { id, fromCache: false }; + } catch (error) { + this.logger.traceJson("Item build failed", { + type: fileInfo.type, + name: fileInfo.name, + error: (error as Error).message, + }); + throw new ItemBuildError( + `Failed to build ${fileInfo.type} "${fileInfo.name}": ${ + (error as Error).message + }`, + fileInfo.type, + fileInfo.name, + error as Error, + ); + } + } + + private async tryUseCache( + fileInfo: FileInfo, + logger: ConsoleLogger, + ): Promise { + this.logger.traceJson("Checking cache for item", { + type: fileInfo.type, + name: fileInfo.name, + }); + + const cacheKey = `${fileInfo.type}:${fileInfo.name}`; + const cached = this.cache.get(cacheKey); + + if (!cached) { + this.logger.traceJson("No cached entry found", { cacheKey }); + return null; + } + + this.logger.traceJson("Found cached entry", { + cacheKey, + cachedId: cached.id, + cachedHash: `${cached.hash?.substring(0, 8)}...`, + }); + + try { + this.logger.trace("Computing current hash for cache validation"); + const currentHash = await this.computeHash(fileInfo); + + if (currentHash === cached.hash) { + logger.debug( + `Using cached ${fileInfo.type} ID: ${cached.id} (hash match)`, + ); + this.logger.traceJson("Cache validation successful", { + hash: `${currentHash.substring(0, 8)}...`, + }); + return cached.id; + } else { + logger.debug( + `Cache invalidated for ${fileInfo.type} "${fileInfo.name}" (hash mismatch)`, + ); + this.logger.traceJson("Cache validation failed", { + currentHash: `${currentHash.substring(0, 8)}...`, + cachedHash: `${cached.hash?.substring(0, 8)}...`, + }); + // Clear hash cache since it's stale + this.hashCache.delete(fileInfo.filePath); + return null; + } + } catch (error: unknown) { + logger.warn( + `Failed to verify cache for ${fileInfo.type} "${fileInfo.name}": ${ + (error as Error).message + }`, + ); + this.logger.traceJson("Cache verification failed with error", { + error: (error as Error).message, + }); + return null; + } + } + + private async computeHash(fileInfo: FileInfo): Promise { + this.logger.traceJson("Computing hash for file", { + filePath: fileInfo.filePath, + }); + + // Check hash cache first + const cachedHash = this.hashCache.get(fileInfo.filePath); + if (cachedHash) { + this.logger.traceJson("Using cached hash", { + filePath: fileInfo.filePath, + hash: `${cachedHash.substring(0, 8)}...`, + }); + return cachedHash; + } + + this.logger.trace("Hash not in cache, computing from builder"); + const builder = await this.loadAndValidateBuilder(fileInfo); + const hash = builder.getHash(); + + // Cache the hash + this.hashCache.set(fileInfo.filePath, hash); + this.logger.traceJson("Hash computed and cached", { + filePath: fileInfo.filePath, + hash: `${hash.substring(0, 8)}...`, + }); + + return hash; + } + + private async buildFromSource( + fileInfo: FileInfo, + logger: ConsoleLogger, + ): Promise { + this.logger.traceJson("Building from source", { + type: fileInfo.type, + name: fileInfo.name, + filePath: fileInfo.filePath, + }); + logger.debug(`Building ${fileInfo.type} from: ${fileInfo.filePath}`); + + const builder = await this.loadAndValidateBuilder(fileInfo); + this.logger.trace("Builder loaded and validated, calling upsert"); + + const id = await builder.upsert(); + this.logger.traceJson("Upsert completed", { id }); + + // Cache the result + this.logger.trace("Caching build result"); + await this.cacheResult(fileInfo, id, builder, logger); + + return id; + } + + private async loadAndValidateBuilder( + fileInfo: FileInfo, + ): Promise { + this.logger.traceJson("Loading and validating builder", { + type: fileInfo.type, + name: fileInfo.name, + filePath: fileInfo.filePath, + }); + + // Load module (with caching) + this.logger.trace("Loading module"); + const moduleExports = await this.loadModule(fileInfo.filePath); + const buildFunction = moduleExports.default; + + if (typeof buildFunction !== "function") { + this.logger.errorJson("Module does not export default function", { + filePath: fileInfo.filePath, + }); + throw new Error( + `${fileInfo.type} "${fileInfo.name}" does not export a default function`, + ); + } + + this.logger.trace("Executing build function"); + // Execute build function + const builder = await buildFunction(this.getContext()); + + this.logger.trace("Validating builder type"); + // Validate builder type + this.validateBuilderType(fileInfo, builder); + + this.logger.traceJson("Builder loaded and validated successfully", { + type: fileInfo.type, + name: fileInfo.name, + builderType: builder.constructor.name, + }); + return builder; + } + + private async loadModule(filePath: string): Promise { + this.logger.traceJson("Loading module", { filePath }); + + // Check module cache first + const cachedModule = this.moduleCache.get(filePath); + if (cachedModule) { + this.logger.traceJson("Using cached module", { filePath }); + return cachedModule; + } + + this.logger.traceJson("Module not in cache, importing", { filePath }); + // Load and cache module + const moduleExports = await import(filePath); + this.moduleCache.set(filePath, moduleExports); + this.logger.traceJson("Module imported and cached", { + filePath, + exportKeys: Object.keys(moduleExports), + }); + + return moduleExports; + } + + private validateBuilderType( + fileInfo: FileInfo, + builder: any, + ): asserts builder is BlockBuilder | ModelBuilder { + this.logger.traceJson("Validating builder type", { + type: fileInfo.type, + name: fileInfo.name, + builderType: builder?.constructor?.name, + }); + + // Check if builder exists and is an object + if (!builder || typeof builder !== "object") { + this.logger.errorJson("Builder is not an object", { + type: fileInfo.type, + name: fileInfo.name, + builderType: typeof builder, + }); + throw new Error( + `${fileInfo.type === "block" ? "Block" : "Model"} "${fileInfo.name}" must return a builder instance`, + ); + } + + const builderTypeName = builder.constructor?.name; + + // Helper function to check if it's a valid builder + const isValidBuilder = (expectedType: string): boolean => { + // Check constructor name + if (builderTypeName !== expectedType) { + return false; + } + + // Check for essential methods that builders should have + const expectedMethods = ["upsert"]; // Add other expected methods + return expectedMethods.every( + (method) => typeof builder[method] === "function", + ); + }; + + if (fileInfo.type === "block") { + if (!isValidBuilder("BlockBuilder")) { + this.logger.errorJson("Invalid builder type for block", { + expected: "BlockBuilder", + actual: builderTypeName, + hasBuildMethod: typeof builder.build === "function", + hasAddFieldMethod: typeof builder.addField === "function", + }); + throw new Error( + `Block "${fileInfo.name}" must return an instance of BlockBuilder`, + ); + } + } + + if (fileInfo.type === "model") { + if (!isValidBuilder("ModelBuilder")) { + this.logger.errorJson("Invalid builder type for model", { + expected: "ModelBuilder", + actual: builderTypeName, + hasBuildMethod: typeof builder.build === "function", + hasAddFieldMethod: typeof builder.addField === "function", + }); + throw new Error( + `Model "${fileInfo.name}" must return an instance of ModelBuilder`, + ); + } + } + + this.logger.trace("Builder type validation passed"); + } + + private async cacheResult( + fileInfo: FileInfo, + id: string, + builder: BlockBuilder | ModelBuilder, + logger: ConsoleLogger, + ): Promise { + this.logger.traceJson("Caching build result", { + type: fileInfo.type, + name: fileInfo.name, + id, + }); + + try { + const cacheKey = `${fileInfo.type}:${fileInfo.name}`; + const hash = builder.getHash(); + + logger.debug( + `Caching ${fileInfo.type} "${fileInfo.name}" with ID: ${id}`, + ); + + await this.cache.set(cacheKey, { id, hash }); + + // Update hash cache + this.hashCache.set(fileInfo.filePath, hash); + + this.logger.traceJson("Build result cached successfully", { + cacheKey, + id, + hash: `${hash.substring(0, 8)}...`, + }); + } catch (error) { + this.logger.errorJson("Failed to cache build result", { + type: fileInfo.type, + name: fileInfo.name, + error: (error as Error).message, + }); + throw error; + } + } +} diff --git a/src/commands/run/RunCommand.ts b/src/commands/run/RunCommand.ts new file mode 100644 index 0000000..a1d24f9 --- /dev/null +++ b/src/commands/run/RunCommand.ts @@ -0,0 +1,623 @@ +import { buildClient } from "@datocms/cma-client-node"; +import type { CacheManager } from "@/cache/CacheManager"; +import type { ConsoleLogger } from "@/logger"; +import type { BuilderContext } from "@/types/BuilderContext"; +import type { DatoBuilderConfig } from "@/types/DatoBuilderConfig"; +import DatoApi from "../../Api/DatoApi"; +import { BuildExecutor } from "./BuildExecutor"; +import { DeletionDetector } from "./DeletionDetector"; +import { DeletionManager } from "./DeletionManager"; +import { DependencyAnalyzer } from "./DependencyAnalyzer"; +import { DependencyResolver } from "./DependencyResolver"; +import { FileDiscoverer } from "./FileDiscoverer"; +import { ItemBuilder } from "./ItemBuilder"; +import type { BuildResult, FileInfo } from "./types"; + +interface RunCommandOptions { + config: Required; + cache: CacheManager; + logger: ConsoleLogger; + enableDeletion?: boolean; + skipDeletionConfirmation?: boolean; + concurrency?: number; +} + +// Custom error for item not found +export class ItemNotFoundError extends Error { + constructor( + message: string, + public readonly itemType: string, + public readonly itemName: string, + public readonly availableItems: string[], + ) { + super(message); + this.name = "ItemNotFoundError"; + } +} + +export class RunCommand { + private readonly config: Required; + private readonly cache: CacheManager; + private readonly logger: ConsoleLogger; + private readonly fileDiscoverer: FileDiscoverer; + private readonly dependencyAnalyzer: DependencyAnalyzer; + private readonly dependencyResolver: DependencyResolver; + private readonly itemBuilder: ItemBuilder; + private readonly buildExecutor: BuildExecutor; + private readonly deletionDetector: DeletionDetector; + private readonly deletionManager: DeletionManager; + private readonly enableDeletion: boolean; + private readonly skipDeletionConfirmation: boolean; + private readonly concurrency: number; + + private fileMap: Map | null = null; + + constructor({ + config, + cache, + logger, + enableDeletion = true, + skipDeletionConfirmation = false, + concurrency = 1, + }: RunCommandOptions) { + this.logger = logger; + + this.logger.traceJson("Initializing RunCommand", { + config: { + logLevel: config.logLevel, + apiToken: config.apiToken ? "***" : "undefined", + blocksPath: config.blocksPath, + modelsPath: config.modelsPath, + }, + enableDeletion, + skipDeletionConfirmation, + }); + + this.config = config; + this.cache = cache; + this.enableDeletion = enableDeletion; + this.skipDeletionConfirmation = skipDeletionConfirmation; + this.concurrency = concurrency; + + this.fileDiscoverer = new FileDiscoverer( + this.config.blocksPath, + this.config.modelsPath, + logger, + ); + + this.dependencyAnalyzer = new DependencyAnalyzer(config, logger); + this.dependencyResolver = new DependencyResolver(logger); + this.itemBuilder = new ItemBuilder(cache, logger, () => this.getContext()); + this.buildExecutor = new BuildExecutor(this.itemBuilder, logger); + this.deletionDetector = new DeletionDetector(cache, logger); + + const datoApi = new DatoApi(buildClient({ apiToken: config.apiToken }), logger); + this.deletionManager = new DeletionManager(datoApi, cache, logger); + + this.logger.trace("RunCommand initialized successfully"); + } + + public async execute(): Promise { + this.logger.trace("Starting RunCommand execution"); + + // File discovery + this.fileMap = await this.fileDiscoverer.discoverFiles(); + this.logger.traceJson("File discovery completed", { + fileCount: this.fileMap.size, + }); + + if (this.fileMap.size === 0) { + this.logger.info("No files found to process"); + return; + } + + // Dependency analysis + this.logger.trace("Starting dependency analysis"); + await this.dependencyAnalyzer.analyzeDependencies(this.fileMap); + this.logger.trace("Dependency analysis completed"); + + // Build order resolution + this.logger.trace("Resolving build order"); + const buildOrder = this.dependencyResolver.topologicalSort(this.fileMap); + this.logger.traceJson("Build order resolved", { + buildOrder: buildOrder.map((key) => this.fileMap?.get(key)?.name || key), + }); + + // Build execution + this.logger.trace("Starting build execution"); + + const results = await this.buildExecutorWithoutProgress( + this.fileMap, + buildOrder, + ); + + this.logger.traceJson("Build execution completed", { + resultCount: results.length, + }); + + // Process results + this.logger.trace("Processing build results"); + + if (this.enableDeletion) { + this.logger.trace("Starting deletion detection and handling"); + await this.handleDeletions(); + } + } + + /** + * Execute build with simple logging + */ + private async buildExecutorWithoutProgress( + fileMap: Map, + buildOrder: string[], + ): Promise { + if (this.concurrency <= 1) { + return this.buildSequentially(fileMap, buildOrder); + } + + return this.buildConcurrently(fileMap, buildOrder); + } + + /** + * Original sequential build logic + */ + private async buildSequentially( + fileMap: Map, + buildOrder: string[], + ): Promise { + const results: BuildResult[] = []; + + for (const fileKey of buildOrder) { + const fileInfo = fileMap.get(fileKey); + if (!fileInfo) { + this.logger.warn(`File info not found for key: ${fileKey}`); + continue; + } + + this.logger.info(`Building ${fileInfo.type}: ${fileInfo.name}`); + + try { + // Build the item + const result = await this.buildExecutor.getOrBuildItem( + fileKey, + fileInfo, + ); + + const status = result.fromCache ? "(from cache)" : "(built)"; + this.logger.success(`${fileInfo.type}: ${fileInfo.name} ${status}`); + + results.push({ + success: true, + fromCache: result.fromCache, + type: fileInfo.type, + name: fileInfo.name, + }); + } catch (error: unknown) { + this.logger.error( + `${fileInfo.type}: ${fileInfo.name} - ${error instanceof Error ? error.message : String(error)}`, + ); + + results.push({ + success: false, + fromCache: false, + type: fileInfo.type, + name: fileInfo.name, + error: error instanceof Error ? error : new Error(String(error)), + }); + } + } + + return results; + } + + /** + * Concurrent build execution that respects dependencies + */ + private async buildConcurrently( + fileMap: Map, + buildOrder: string[], + ): Promise { + const results: BuildResult[] = []; + const completed = new Set(); + const inProgress = new Map>(); + const pending = new Set(buildOrder); + + // Build dependency map for quick lookup + const dependsOn = new Map>(); + for (const [fileKey, fileInfo] of fileMap) { + dependsOn.set(fileKey, new Set(fileInfo.dependencies || [])); + } + + this.logger.traceJson("Starting concurrent build", { + totalItems: buildOrder.length, + concurrency: this.concurrency, + }); + + // Keep building until all items are processed + while (pending.size > 0 || inProgress.size > 0) { + // Find items ready to build (dependencies satisfied) + const readyToBuild = Array.from(pending).filter((fileKey) => { + const deps = dependsOn.get(fileKey) || new Set(); + return Array.from(deps).every((dep) => completed.has(dep)); + }); + + // Start builds up to concurrency limit + const slotsAvailable = this.concurrency - inProgress.size; + const itemsToStart = readyToBuild.slice(0, slotsAvailable); + + for (const fileKey of itemsToStart) { + pending.delete(fileKey); + const buildPromise = this.buildSingleItem(fileMap, fileKey); + inProgress.set(fileKey, buildPromise); + + const fileInfo = fileMap.get(fileKey); + if (fileInfo) { + this.logger.info(`Building ${fileInfo.type}: ${fileInfo.name}`); + } + } + + // If we have builds in progress, wait for at least one to complete + if (inProgress.size > 0) { + // Wait for ALL in-progress builds to complete, not just one + const inProgressEntries = Array.from(inProgress.entries()); + const completedBuilds = await Promise.allSettled( + inProgressEntries.map(async ([fileKey, promise]) => ({ + fileKey, + result: await promise, + })), + ); + + // Process all completed builds + for (const buildResult of completedBuilds) { + if (buildResult.status === "fulfilled") { + const { fileKey, result } = buildResult.value; + + // Remove from in progress and add to completed + inProgress.delete(fileKey); + completed.add(fileKey); + results.push(result); + + const fileInfo = fileMap.get(fileKey); + if (fileInfo) { + if (result.success) { + const status = result.fromCache ? "(from cache)" : "(built)"; + this.logger.success( + `${fileInfo.type}: ${fileInfo.name} ${status}`, + ); + } else { + const errorMessage = + result.error instanceof Error + ? result.error.message + : String(result.error); + + this.logger.error( + `${fileInfo.type}: ${fileInfo.name} - ${errorMessage}`, + ); + } + } + } else { + // Handle rejected promises + this.logger.error(`Build promise rejected: ${buildResult.reason}`); + } + } + + // Clear the inProgress map since we've processed all builds + inProgress.clear(); + } + + // Safety check to prevent infinite loops + if (pending.size > 0 && inProgress.size === 0) { + const readyToBuildAfterCompletion = Array.from(pending).filter( + (fileKey) => { + const deps = dependsOn.get(fileKey) || new Set(); + return Array.from(deps).every((dep) => completed.has(dep)); + }, + ); + + if (readyToBuildAfterCompletion.length === 0) { + this.logger.error( + "Dependency deadlock detected. Some items cannot be built due to circular or missing dependencies.", + ); + const remainingItems = Array.from(pending).map((key) => { + const fileInfo = fileMap.get(key); + const deps = dependsOn.get(key) || new Set(); + const missingDeps = Array.from(deps).filter( + (dep) => !completed.has(dep), + ); + return fileInfo + ? `${fileInfo.type}:${fileInfo.name} (missing deps: ${missingDeps.join(", ")})` + : key; + }); + this.logger.error(`Remaining items: ${remainingItems.join(", ")}`); + break; + } + } + } + + this.logger.traceJson("Concurrent build completed", { + totalResults: results.length, + successful: results.filter((r) => r.success).length, + failed: results.filter((r) => !r.success).length, + }); + + return results; + } + + /** + * Build a single item and return the result + */ + private async buildSingleItem( + fileMap: Map, + fileKey: string, + ): Promise { + const fileInfo = fileMap.get(fileKey); + if (!fileInfo) { + return { + success: false, + fromCache: false, + type: "unknown", + name: fileKey, + error: new Error(`File info not found for key: ${fileKey}`), + }; + } + + try { + const result = await this.buildExecutor.getOrBuildItem(fileKey, fileInfo); + return { + success: true, + fromCache: result.fromCache, + type: fileInfo.type, + name: fileInfo.name, + }; + } catch (error: unknown) { + return { + success: false, + fromCache: false, + type: fileInfo.type, + name: fileInfo.name, + error: error instanceof Error ? error : new Error(String(error)), + }; + } + } + + private getContext(): BuilderContext { + this.logger.trace("Creating builder context"); + return { + config: this.config, + getBlock: (name: string) => this.getItemId("block", name), + getModel: (name: string) => this.getItemId("model", name), + }; + } + + private async getItemId( + type: "block" | "model", + name: string, + ): Promise { + this.logger.traceJson("Getting item ID", { type, name }); + + const cacheKey = `${type}:${name}`; + const cached = this.cache.get(cacheKey); + + if (cached) { + this.logger.traceJson("Item found in cache", { + type, + name, + id: cached.id, + }); + return cached.id; + } + + this.logger.traceJson("Item not in cache, attempting to build", { + type, + name, + }); + + // If not in cache, try to build it using BuildExecutor + if (this.fileMap) { + const fileKey = this.findFileKeyByName(type, name); + + if (fileKey) { + this.logger.traceJson("Found file key for item", { + type, + name, + fileKey, + }); + const fileInfo = this.fileMap.get(fileKey); + + if (fileInfo) { + try { + this.logger.debug(`Building dependency ${type} "${name}"`); + const result = await this.buildExecutor.getOrBuildItem( + fileKey, + fileInfo, + ); + this.logger.traceJson("Dependency built successfully", { + type, + name, + id: result.id, + }); + return result.id; + } catch (error) { + this.logger.errorJson( + `Failed to build dependency ${type} "${name}"`, + error instanceof Error ? error : new Error(String(error)), + ); + throw error; + } + } + } else { + this.logger.traceJson("No file key found for item", { type, name }); + } + } else { + this.logger.traceJson("No file map available for item lookup", { + type, + name, + }); + } + + // Generate helpful error message with available items + this.logger.traceJson("Generating error message for missing item", { + type, + name, + }); + const availableItems = this.getAvailableItems(type); + const errorMessage = this.buildItemNotFoundMessage( + type, + name, + availableItems, + ); + + this.logger.traceJson("Item not found", { type, name, availableItems }); + throw new ItemNotFoundError(errorMessage, type, name, availableItems); + } + + private findFileKeyByName( + type: "block" | "model", + name: string, + ): string | null { + this.logger.traceJson("Finding file key by name", { type, name }); + + if (!this.fileMap) { + this.logger.trace("No file map available for lookup"); + return null; + } + + for (const [fileKey, fileInfo] of this.fileMap.entries()) { + if (fileInfo.type === type && fileInfo.name === name) { + this.logger.traceJson("Found file key", { type, name, fileKey }); + return fileKey; + } + } + + this.logger.traceJson("File key not found", { type, name }); + return null; + } + + private getAvailableItems(type: "block" | "model"): string[] { + this.logger.traceJson("Getting available items", { type }); + + const availableItems: string[] = []; + + // Get items from cache + const cachedItems = Array.from(this.cache.keys()) + .filter((key) => key.startsWith(`${type}:`)) + .map((key) => key.substring(type.length + 1)); + + // Get items from file map + const fileMapItems = this.fileMap + ? Array.from(this.fileMap.values()) + .filter((fileInfo) => fileInfo.type === type) + .map((fileInfo) => fileInfo.name) + : []; + + // Combine and deduplicate + availableItems.push(...cachedItems, ...fileMapItems); + const result = [...new Set(availableItems)].sort(); + + this.logger.traceJson("Available items retrieved", { + type, + cachedCount: cachedItems.length, + fileMapCount: fileMapItems.length, + totalCount: result.length, + }); + return result; + } + + private buildItemNotFoundMessage( + type: "block" | "model", + name: string, + availableItems: string[], + ): string { + this.logger.traceJson("Building item not found message", { + type, + name, + availableItemsCount: availableItems.length, + }); + + const baseMessage = `Cannot find ${type} with name "${name}".`; + + if (availableItems.length === 0) { + this.logger.trace("No available items for error message"); + return `${baseMessage} No ${type}s are available.`; + } + + // If there are similar names, suggest them + const similarItems = this.findSimilarItems(name, availableItems); + + if (similarItems.length > 0) { + this.logger.traceJson("Found similar items for suggestion", { + similarItems, + }); + return `${baseMessage} Did you mean one of these: ${similarItems.join( + ", ", + )}? Available ${type}s: ${availableItems.join(", ")}`; + } + + this.logger.trace("No similar items found, returning basic error message"); + return `${baseMessage} Available ${type}s: ${availableItems.join(", ")}`; + } + + private findSimilarItems(target: string, items: string[]): string[] { + const targetLower = target.toLowerCase(); + + // Find items that contain the target or vice versa + const similar = items.filter((item) => { + const itemLower = item.toLowerCase(); + return itemLower.includes(targetLower) || targetLower.includes(itemLower); + }); + + // Sort by similarity (shorter matches first) + return similar.sort((a, b) => a.length - b.length).slice(0, 3); + } + + private async handleDeletions(): Promise { + if (!this.fileMap) { + this.logger.warn("No file map available for deletion detection"); + return; + } + + this.logger.trace("Starting deletion detection"); + + const deletionSummary = this.deletionDetector.detectDeletions(this.fileMap); + + if (deletionSummary.total === 0) { + this.logger.debug("No deletions detected"); + return; + } + + this.logger.traceJson("Deletions detected", { + total: deletionSummary.total, + blocks: deletionSummary.blocks.length, + models: deletionSummary.models.length, + }); + + const allCandidates = [ + ...deletionSummary.blocks, + ...deletionSummary.models, + ]; + const { safe, unsafe } = this.deletionDetector.filterSafeDeletions( + allCandidates, + this.fileMap, + ); + + const deletionOptions = { + skipConfirmation: this.skipDeletionConfirmation, + }; + + const results = await this.deletionManager.handleDeletions( + deletionSummary, + safe, + unsafe, + deletionOptions, + ); + + const successful = results.filter((r) => r.success).length; + const failed = results.filter((r) => !r.success).length; + + if (results.length > 0) { + this.logger.info( + `Deletion process completed: ${successful} successful, ${failed} failed`, + ); + } + } +} diff --git a/src/commands/run/types.ts b/src/commands/run/types.ts new file mode 100644 index 0000000..b778f8b --- /dev/null +++ b/src/commands/run/types.ts @@ -0,0 +1,14 @@ +export interface FileInfo { + name: string; + type: "block" | "model"; + filePath: string; + dependencies: Set; +} + +export interface BuildResult { + name: string; + type: "block" | "model" | "unknown"; + fromCache: boolean; + success: boolean; + error?: Error | string; +} diff --git a/src/config/ConfigParser.test.ts b/src/config/ConfigParser.test.ts new file mode 100644 index 0000000..c0a95b4 --- /dev/null +++ b/src/config/ConfigParser.test.ts @@ -0,0 +1,403 @@ +import fs from "node:fs"; +import path from "node:path"; +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; +import { createMockLogger } from "@tests/utils/mockLogger"; +import { ConfigParser } from "@/config/ConfigParser"; +import type { DatoBuilderConfig } from "../../src"; + +// Mock fs and path modules +jest.mock("node:fs"); +jest.mock("node:path"); + +describe("ConfigParser", () => { + let configParser: ConfigParser; + let mockFs: jest.Mocked; + let mockPath: jest.Mocked; + + beforeEach(() => { + // Reset all mocks + jest.clearAllMocks(); + jest.resetModules(); + + // Setup mocked modules + mockFs = fs as jest.Mocked; + mockPath = path as jest.Mocked; + + // Setup path.resolve mock to return predictable paths + mockPath.resolve.mockImplementation((...args) => args.join("/")); + + // Mock process.cwd() + jest.spyOn(process, "cwd").mockReturnValue("/mock/cwd"); + + configParser = new ConfigParser(createMockLogger()); + }); + + describe("loadConfig", () => { + it("should load config from .js file when it exists", async () => { + const mockConfig: DatoBuilderConfig = { + apiToken: "test-token", + overwriteExistingFields: true, + modelApiKeySuffix: "custom-model", + }; + + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + // Mock the module before the dynamic import + jest.doMock("/mock/cwd/dato-builder.config.js", () => mockConfig, { + virtual: true, + }); + + const result = await configParser.loadConfig(); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + expect(result).toEqual({ + apiToken: "test-token", + overwriteExistingFields: true, + modelApiKeySuffix: "custom-model", + blockApiKeySuffix: "block", + blocksPath: "/mock/cwd/datocms/blocks", + modelsPath: "/mock/cwd/datocms/models", + syncBlocksPath: "/mock/cwd/datocms/.generated/blocks", + syncModelsPath: "/mock/cwd/datocms/.generated/models", + logLevel: 2, + }); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should load config from .ts file when .js doesn't exist", async () => { + const mockConfig: DatoBuilderConfig = { + apiToken: "test-token-ts", + logLevel: 3, + }; + + // Mock fs.existsSync to return false for .js but true for .ts + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.ts"; + }); + + // Mock the module before the dynamic import + jest.doMock("/mock/cwd/dato-builder.config.ts", () => mockConfig, { + virtual: true, + }); + + const result = await configParser.loadConfig(); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.ts", + ); + expect(result).toEqual({ + apiToken: "test-token-ts", + overwriteExistingFields: false, + modelApiKeySuffix: "model", + blockApiKeySuffix: "block", + blocksPath: "/mock/cwd/datocms/blocks", + modelsPath: "/mock/cwd/datocms/models", + syncBlocksPath: "/mock/cwd/datocms/.generated/blocks", + syncModelsPath: "/mock/cwd/datocms/.generated/models", + logLevel: 3, + }); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.ts"); + }); + + it("should throw error when no config file exists", async () => { + // Mock fs.existsSync to return false for both files + mockFs.existsSync.mockReturnValue(false); + + await expect(configParser.loadConfig()).rejects.toThrow( + "No dato-builder config file found", + ); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.ts", + ); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should throw error when config file has no default export", async () => { + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + jest.doMock( + "/mock/cwd/dato-builder.config.js", + () => { + return { + __esModule: true, + }; // No default export + }, + { + virtual: true, + }, + ); + + await expect(configParser.loadConfig()).rejects.toThrow( + "Unable to load dato-builder config file", + ); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + + await expect(configParser.loadConfig()).rejects.toThrow( + "Unable to load dato-builder config file", + ); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should throw error when config file has undefined default export", async () => { + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + jest.doMock( + "/mock/cwd/dato-builder.config.js", + () => { + return { + __esModule: true, + default: undefined, // Explicitly undefined + }; + }, + { + virtual: true, + }, + ); + + await expect(configParser.loadConfig()).rejects.toThrow( + "Unable to load dato-builder config file", + ); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should throw error when apiToken is missing", async () => { + const mockConfig: Partial = { + overwriteExistingFields: true, + // Missing apiToken + }; + + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + // Mock the dynamic import + jest.doMock( + "/mock/cwd/dato-builder.config.js", + () => { + return { + default: mockConfig, + }; + }, + { + virtual: true, + }, + ); + + await expect(configParser.loadConfig()).rejects.toThrow( + "Validation error: Missing apiToken", + ); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should throw error when apiToken is empty string", async () => { + const mockConfig: DatoBuilderConfig = { + apiToken: "", + overwriteExistingFields: true, + }; + + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + // Mock the dynamic import + jest.doMock( + "/mock/cwd/dato-builder.config.js", + () => { + return { + default: mockConfig, + }; + }, + { + virtual: true, + }, + ); + + await expect(configParser.loadConfig()).rejects.toThrow( + "Validation error: Missing apiToken", + ); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should merge user config with defaults correctly", async () => { + const mockConfig: DatoBuilderConfig = { + apiToken: "test-token", + blocksPath: "/custom/blocks/path", + // Other fields should use defaults + }; + + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + // Mock the module before the dynamic import + jest.doMock("/mock/cwd/dato-builder.config.js", () => mockConfig, { + virtual: true, + }); + + const result = await configParser.loadConfig(); + + expect(result).toEqual({ + apiToken: "test-token", + overwriteExistingFields: false, // default + modelApiKeySuffix: "model", // default + blockApiKeySuffix: "block", // default + blocksPath: "/custom/blocks/path", // user override + modelsPath: "/mock/cwd/datocms/models", // default + syncBlocksPath: "/mock/cwd/datocms/.generated/blocks", // default + syncModelsPath: "/mock/cwd/datocms/.generated/models", // default + logLevel: 2, // default + }); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should handle all custom configuration options", async () => { + const mockConfig: DatoBuilderConfig = { + apiToken: "custom-token", + overwriteExistingFields: true, + modelApiKeySuffix: "custom-model", + blockApiKeySuffix: "custom-block", + blocksPath: "/custom/blocks", + modelsPath: "/custom/models", + syncBlocksPath: "/custom/sync/blocks", + syncModelsPath: "/custom/sync/models", + logLevel: 0, + }; + + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + // Mock the module before the dynamic import + jest.doMock("/mock/cwd/dato-builder.config.js", () => mockConfig, { + virtual: true, + }); + + const result = await configParser.loadConfig(); + + expect(result).toEqual(mockConfig); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + + it("should prefer .js file over .ts file when both exist", async () => { + const mockConfig: DatoBuilderConfig = { + apiToken: "js-token", + }; + + // Mock fs.existsSync to return true for both files + mockFs.existsSync.mockReturnValue(true); + + // Mock the module before the dynamic import + jest.doMock("/mock/cwd/dato-builder.config.ts", () => mockConfig, { + virtual: true, + }); + + // Mock the module before the dynamic import + jest.doMock("/mock/cwd/dato-builder.config.js", () => mockConfig, { + virtual: true, + }); + + await configParser.loadConfig(); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + expect(mockFs.existsSync).not.toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.ts", + ); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + jest.dontMock("/mock/cwd/dato-builder.config.ts"); + }); + }); + + describe("error handling", () => { + it("should handle filesystem errors when checking file existence", async () => { + // Mock fs.existsSync to throw an error + mockFs.existsSync.mockImplementation(() => { + throw new Error("Filesystem error"); + }); + + await expect(configParser.loadConfig()).rejects.toThrow( + "Filesystem error", + ); + + expect(mockFs.existsSync).toHaveBeenCalledWith( + "/mock/cwd/dato-builder.config.js", + ); + }); + + it("should handle malformed config objects", async () => { + const mockConfig = null; + + // Mock fs.existsSync to return true for .js file + mockFs.existsSync.mockImplementation((filePath) => { + return filePath === "/mock/cwd/dato-builder.config.js"; + }); + + // Mock the dynamic import + jest.doMock("/mock/cwd/dato-builder.config.js", () => mockConfig, { + virtual: true, + }); + + await expect(configParser.loadConfig()).rejects.toThrow( + "Unable to load dato-builder config file", + ); + + // Clean up + jest.dontMock("/mock/cwd/dato-builder.config.js"); + }); + }); +}); diff --git a/src/config/ConfigParser.ts b/src/config/ConfigParser.ts new file mode 100644 index 0000000..29f3123 --- /dev/null +++ b/src/config/ConfigParser.ts @@ -0,0 +1,75 @@ +import fs from "node:fs"; +import path from "node:path"; +import { type ConsoleLogger, LogLevel } from "@/logger"; +import type { DatoBuilderConfig } from "@/types/DatoBuilderConfig"; + +export class ConfigParser { + private readonly logger: ConsoleLogger; + + constructor(logger: ConsoleLogger) { + this.logger = logger; + } + + public async loadConfig(): Promise> { + const configPath = await this.getConfigFilePath(); + + this.logger.debug(`Loading config from ${configPath}`); + + const userConfig = await import(configPath); + + if (!userConfig.default) { + throw new Error("Unable to load dato-builder config file"); + } + + return this.validateConfig({ + ...this.DEFAULTS, + ...(userConfig.default as DatoBuilderConfig), + }); + } + + private get DEFAULTS(): Omit, "apiToken"> { + return { + overwriteExistingFields: false, + modelApiKeySuffix: "model", + blockApiKeySuffix: "block", + blocksPath: path.resolve(process.cwd(), "datocms", "blocks"), + modelsPath: path.resolve(process.cwd(), "datocms", "models"), + syncBlocksPath: path.resolve( + process.cwd(), + "datocms", + ".generated", + "blocks", + ), + syncModelsPath: path.resolve( + process.cwd(), + "datocms", + ".generated", + "models", + ), + logLevel: LogLevel.INFO, + }; + } + + private async getConfigFilePath(): Promise { + const possiblePaths = [ + path.resolve(process.cwd(), "dato-builder.config.js"), + path.resolve(process.cwd(), "dato-builder.config.ts"), + ]; + + for (const configPath of possiblePaths) { + if (fs.existsSync(configPath)) { + return configPath; + } + } + + throw new Error("No dato-builder config file found"); + } + + private validateConfig(config: T): T { + if (!config.apiToken) { + throw new Error("Validation error: Missing apiToken"); + } + + return config; + } +} diff --git a/src/config/index.ts b/src/config/index.ts deleted file mode 100644 index e08bb44..0000000 --- a/src/config/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - buildClient, - type Client as DatoClient, -} from "@datocms/cma-client-node"; -import { loadDatoBuilderConfig } from "./loader"; -import type { DatoBuilderConfig } from "./types"; - -const { apiToken } = loadDatoBuilderConfig() as Required< - Pick ->; - -const clientInstance: DatoClient = buildClient({ apiToken }); - -/** - * Retrieve the singleton DatoCMS CMA client, already configured - * with the project’s apiToken from dato-builder.config. - */ -export function getDatoClient(): DatoClient { - return clientInstance; -} diff --git a/src/config/loader.ts b/src/config/loader.ts deleted file mode 100644 index 6fcef6e..0000000 --- a/src/config/loader.ts +++ /dev/null @@ -1,112 +0,0 @@ -import * as fs from "node:fs"; -import path from "node:path"; -import type { DatoBuilderConfig } from "./types"; - -export function loadDatoBuilderConfig(): DatoBuilderConfig { - const config: Partial = {}; - - // Load config from file if it exists - if (!tryLoadFromConfigFile(config)) { - throw new Error( - "No Dato Builder config found. Please create a file named 'dato-builder.config.js' or 'dato-builder.config.ts' in the current directory.", - ); - } - - // Validate the config - validateConfig(config); - - return config as DatoBuilderConfig; -} - -/** - * Attempts to load config from JS or TS file - */ -function tryLoadFromConfigFile(config: Partial): boolean { - try { - // Try to find config file in current directory with different extensions - const possiblePaths = [ - path.resolve(process.cwd(), "dato-builder.config.js"), - path.resolve(process.cwd(), "dato-builder.config.ts"), - ]; - - for (const filePath of possiblePaths) { - if (fs.existsSync(filePath)) { - return loadConfigFromFile(config, filePath); - } - } - } catch (error) { - console.warn( - `Failed to load config file: ${error instanceof Error ? error.message : String(error)}`, - ); - } - - return false; -} - -/** - * Loads config from the specified JavaScript or TypeScript file - */ -function loadConfigFromFile( - config: Partial, - filePath: string, -): boolean { - const ext = path.extname(filePath); - - // Only support JS and TS files - if (ext !== ".js" && ext !== ".ts") { - console.warn( - `Unsupported config file extension: ${ext}. Only .js and .ts are supported.`, - ); - return false; - } - - try { - // For TS files, ensure ts-node is registered - if (ext === ".ts" && !require.extensions[".ts"]) { - try { - require("ts-node/register"); - } catch (_e) { - throw new Error( - "ts-node is required to load TypeScript config files. Please install it with: npm install -D ts-node", - ); - } - } - - const fileConfig = require(filePath); - // Handle both default exports and regular exports - const config_obj = fileConfig.default || fileConfig; - if (typeof config_obj !== "object") { - throw new Error( - `Invalid config file format. Expected an object, but got ${typeof config_obj}.`, - ); - } - - Object.assign(config, config_obj); - return true; - } catch (error) { - console.warn( - `Failed to import config file ${filePath}: ${error instanceof Error ? error.message : String(error)}`, - ); - return false; - } -} - -/** - * Validates that the config has all required properties - */ -function validateConfig(config: Partial): void { - if (!config.apiToken) { - throw new Error( - "API token is required. Please provide it in the config file.", - ); - } - - // Set default values - if (config.overwriteExistingFields === undefined) { - config.overwriteExistingFields = false; - } - - if (config.debug === undefined) { - config.debug = false; - } -} diff --git a/src/config/types.ts b/src/config/types.ts deleted file mode 100644 index 78ef827..0000000 --- a/src/config/types.ts +++ /dev/null @@ -1,35 +0,0 @@ -export interface DatoBuilderConfig { - /** - * Your DatoCMS Content Management API token. - * - * You can find this in your DatoCMS project settings under β€œAPI tokens”. - * This value is required. - */ - apiToken: string; - - /** - * Whether to overwrite existing fields in DatoCMS when syncing. - * - * - `false` (default): New fields will be created and removed fields - * deleted, but any fields that already exist (matched by API key) - * will be left untouched. - * - * - `true`: Fields with matching API keys will be updated to match - * your code definitions, overwriting any manual changes made via - * the DatoCMS dashboard. - */ - overwriteExistingFields?: boolean; - - /** - * Activate the "debug" mode. This will log more information to the console. - */ - debug?: boolean; - /** - * Model API Key Suffix - */ - modelApiKeySuffix?: string; - /** - * Block API Key Suffix - */ - blockApiKeySuffix?: string; -} diff --git a/src/datocms/blocks/TestReferenceBlock.ts b/src/datocms/blocks/TestReferenceBlock.ts new file mode 100644 index 0000000..dede957 --- /dev/null +++ b/src/datocms/blocks/TestReferenceBlock.ts @@ -0,0 +1,15 @@ +import BlockBuilder from "../../BlockBuilder"; +import type { BuilderContext } from "../../types/BuilderContext"; + +export default function buildTestReferenceBlock({ config }: BuilderContext) { + return new BlockBuilder({ + name: "Test Reference Block", + config, + options: { + api_key: "test_reference_block", + hint: "Simple block for testing references", + }, + }) + .addText({ label: "Title", body: { api_key: "title" } }) + .addMultiLineText({ label: "Content", body: { api_key: "content" } }); +} diff --git a/src/datocms/models/TestReferenceModel.ts b/src/datocms/models/TestReferenceModel.ts new file mode 100644 index 0000000..c255b00 --- /dev/null +++ b/src/datocms/models/TestReferenceModel.ts @@ -0,0 +1,21 @@ +import ModelBuilder from "../../ModelBuilder"; +import type { BuilderContext } from "../../types/BuilderContext"; + +export default function buildTestReferenceModel({ config }: BuilderContext) { + return new ModelBuilder({ + name: "Test Reference Model", + config, + body: { + api_key: "test_reference_model", + hint: "Simple model for testing references", + collection_appearance: "table", + ordering_field: null, + ordering_direction: null, + tree: false, + draft_mode_active: false, + all_locales_required: false, + }, + }) + .addText({ label: "Name", body: { api_key: "name" } }) + .addSlug({ label: "Slug", body: { api_key: "slug" } }); +} diff --git a/src/index.ts b/src/index.ts index 1a37fdf..fc2568f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export { default as BlockBuilder } from "./BlockBuilder"; -export * from "./config/types"; export * from "./ItemTypeBuilder"; export { default as ModelBuilder } from "./ModelBuilder"; +export * from "./types/BuilderContext"; +export * from "./types/DatoBuilderConfig"; diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..2918e79 --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,388 @@ +export enum LogLevel { + NONE = -1, + ERROR = 0, + WARN = 1, + INFO = 2, + DEBUG = 3, + TRACE = 4, +} + +export interface LogContext { + model?: string; + block?: string; + itemType?: string; + field?: string; + operation?: string; + [key: string]: string | number | boolean | undefined; +} + +export interface LoggerOptions { + prefix?: string; + colors?: boolean; + timestamp?: boolean; + contextOrder?: string[]; + maxContextLength?: number; + prettyJson?: boolean; +} + +export class ConsoleLogger { + private readonly prefix: string = ""; + private readonly enableColors: boolean; + private readonly showTimestamp: boolean; + private readonly contextOrder: string[]; + private readonly maxContextLength: number; + private readonly prettyJson: boolean; + private readonly timers = new Map(); + + private readonly colors = { + reset: "\x1b[0m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + gray: "\x1b[90m", + cyan: "\x1b[36m", + magenta: "\x1b[35m", + white: "\x1b[37m", + bold: "\x1b[1m", + dim: "\x1b[2m", + }; + + private readonly level: LogLevel; + private readonly context: LogContext; + + constructor( + level: LogLevel = LogLevel.INFO, + context: LogContext = {}, + options: LoggerOptions = {}, + ) { + this.level = level; + this.context = context; + this.prefix = options.prefix || ""; + this.enableColors = options.colors !== false; + this.showTimestamp = options.timestamp || false; + this.contextOrder = options.contextOrder || [ + "model", + "block", + "itemType", + "field", + "operation", + ]; + this.maxContextLength = options.maxContextLength || 30; + this.prettyJson = options.prettyJson !== false; + } + + // Create a child logger with additional context + child(additionalContext: LogContext): ConsoleLogger { + return new ConsoleLogger( + this.level, + { ...this.context, ...additionalContext }, + { + prefix: this.prefix, + colors: this.enableColors, + timestamp: this.showTimestamp, + contextOrder: this.contextOrder, + maxContextLength: this.maxContextLength, + prettyJson: this.prettyJson, + }, + ); + } + + // Update log level + setLevel(level: LogLevel): void { + (this as any).level = level; + } + + private shouldLog(level: LogLevel): boolean { + return this.level >= level; + } + + private colorize(color: string, text: string): string { + if (!this.enableColors) return text; + return `${color}${text}${this.colors.reset}`; + } + + private formatTimestamp(): string { + if (!this.showTimestamp) return ""; + const now = new Date(); + const time = now.toTimeString().split(" ")[0]; + return this.colorize(this.colors.dim, `[${time}] `); + } + + private truncateContext(text: string, maxLength: number): string { + if (text.length <= maxLength) return text; + return `${text.substring(0, maxLength - 3)}...`; + } + + private formatContext(): string { + const contextParts: string[] = []; + + // Add contexts in specified order + for (const key of this.contextOrder) { + const value = this.context[key]; + if (value !== undefined) { + let formatted = ""; + let color = this.colors.gray; + + switch (key) { + case "model": + formatted = `${value}`; + color = this.colors.cyan; + break; + case "block": + formatted = `${value}`; + color = this.colors.magenta; + break; + case "itemType": + formatted = `${value}`; + color = this.colors.blue; + break; + case "field": + formatted = `${value}`; + color = this.colors.yellow; + break; + case "operation": + formatted = `${value}`; + color = this.colors.green; + break; + default: + formatted = `${key}:${value}`; + } + + const truncated = this.truncateContext( + formatted, + this.maxContextLength, + ); + contextParts.push(this.colorize(color, `[${truncated}]`)); + } + } + + // Add any remaining context keys not in the order + for (const [key, value] of Object.entries(this.context)) { + if (!this.contextOrder.includes(key) && value !== undefined) { + const formatted = this.truncateContext( + `${key}:${value}`, + this.maxContextLength, + ); + contextParts.push(this.colorize(this.colors.gray, `[${formatted}]`)); + } + } + + return contextParts.length > 0 ? `${contextParts.join(" ")} ` : ""; + } + + private formatMessage(level: string, color: string, ...msg: any[]): string { + const timestamp = this.formatTimestamp(); + const context = this.formatContext(); + const prefixStr = this.prefix ?? ""; + const prefixText = prefixStr + ? `${this.colorize(this.colors.dim, prefixStr)} ` + : ""; + + const bodyText = `${level}: ${msg.join(" ")}`; + const coloredBody = this.colorize(color, bodyText); + + return `${timestamp} ${prefixText}${context}${coloredBody}`; + } + + error(...msg: any[]) { + if (!this.shouldLog(LogLevel.ERROR)) return; + const formatted = this.formatMessage("ERROR", this.colors.red, ...msg); + console.error(formatted); + } + + warn(...msg: any[]) { + if (!this.shouldLog(LogLevel.WARN)) return; + const formatted = this.formatMessage("WARN", this.colors.yellow, ...msg); + console.warn(formatted); + } + + info(...msg: any[]) { + if (!this.shouldLog(LogLevel.INFO)) return; + const formatted = this.formatMessage("INFO", this.colors.blue, ...msg); + console.info(formatted); + } + + debug(...msg: any[]) { + if (!this.shouldLog(LogLevel.DEBUG)) return; + const formatted = this.formatMessage("DEBUG", this.colors.gray, ...msg); + console.debug(formatted); + } + + trace(...msg: any[]) { + if (!this.shouldLog(LogLevel.TRACE)) return; + const formatted = this.formatMessage("TRACE", this.colors.dim, ...msg); + console.debug(formatted); + } + + success(...msg: any[]) { + if (!this.shouldLog(LogLevel.INFO)) return; + const formatted = this.formatMessage("SUCCESS", this.colors.green, ...msg); + console.log(formatted); + } + + // Enhanced JSON logging with better formatting + json(message: string, obj: any, level: LogLevel = LogLevel.DEBUG) { + if (!this.shouldLog(level)) return; + + const levelMap = { + [LogLevel.NONE]: { + method: () => {}, + color: this.colors.reset, + }, + [LogLevel.ERROR]: { + method: console.error, + color: this.colors.red, + }, + [LogLevel.WARN]: { + method: console.warn, + color: this.colors.yellow, + }, + [LogLevel.INFO]: { + method: console.info, + color: this.colors.blue, + }, + [LogLevel.DEBUG]: { + method: console.debug, + color: this.colors.gray, + }, + [LogLevel.TRACE]: { + method: console.debug, + color: this.colors.dim, + }, + }; + + const { method, color } = levelMap[level]; + const timestamp = this.formatTimestamp(); + const context = this.formatContext(); + const prefixText = this.colorize(this.colors.dim, this.prefix); + const levelText = this.colorize(color, LogLevel[level]); + + // Format the header + const header = `${timestamp} ${prefixText} ${context}${levelText}: ${message}`; + method(header); + + // Format the JSON with proper indentation and colors + if (this.prettyJson) { + const jsonStr = JSON.stringify(obj, null, 2); + const coloredJson = this.enableColors + ? this.colorize(this.colors.dim, jsonStr) + : jsonStr; + method(coloredJson); + } else { + // Compact but readable format + const compactJson = JSON.stringify(obj, null, 0) + .replace(/,/g, ", ") + .replace(/:/g, ": ") + .replace(/\{/g, "{ ") + .replace(/\}/g, " }"); + const coloredJson = this.enableColors + ? this.colorize(this.colors.dim, compactJson) + : compactJson; + method(coloredJson); + } + } + + debugJson(message: string, obj: any) { + this.json(message, obj, LogLevel.DEBUG); + } + + infoJson(message: string, obj: any) { + this.json(message, obj, LogLevel.INFO); + } + + errorJson(message: string, obj: any) { + this.json(message, obj, LogLevel.ERROR); + } + + traceJson(message: string, obj: any) { + this.json(message, obj, LogLevel.TRACE); + } + + time(label: string) { + this.timers.set(label, Date.now()); + this.debug(`Timer started: ${label}`); + } + + timeEnd(label: string) { + const startTime = this.timers.get(label); + if (startTime === undefined) { + this.warn(`Timer "${label}" was not started`); + return; + } + + const elapsed = Date.now() - startTime; + this.timers.delete(label); + this.debug(`Timer ended: ${label} (${elapsed}ms)`); + return elapsed; + } + + progress(current: number, total: number, item?: string) { + if (!this.shouldLog(LogLevel.INFO)) return; + + const percentage = Math.round((current / total) * 100); + const bar = + "β–ˆ".repeat(Math.floor(percentage / 5)) + + "β–‘".repeat(20 - Math.floor(percentage / 5)); + const itemText = item ? ` - ${item}` : ""; + + const formatted = this.formatMessage( + "PROGRESS", + this.colors.blue, + `[${bar}] ${percentage}% (${current}/${total})${itemText}`, + ); + + // Use \r to overwrite the same line for progress updates + process.stdout.write(`\r${formatted}`); + + // If we're at 100%, add a newline + if (current === total) { + process.stdout.write("\n"); + } + } + + banner(title: string, details?: string[]) { + if (!this.shouldLog(LogLevel.INFO)) return; + + const width = 60; + const border = "=".repeat(width); + const titleLine = `${title}` + .padStart((width + title.length) / 2) + .padEnd(width); + + console.log(this.colorize(this.colors.blue + this.colors.bold, border)); + console.log(this.colorize(this.colors.blue + this.colors.bold, titleLine)); + + if (details) { + for (const detail of details) { + const detailLine = `${detail}`.padEnd(width); + console.log(this.colorize(this.colors.blue, detailLine)); + } + } + + console.log(this.colorize(this.colors.blue + this.colors.bold, border)); + } + + table(data: Record[]) { + if (!this.shouldLog(LogLevel.INFO)) return; + console.table(data); + } + + group(label: string) { + if (!this.shouldLog(LogLevel.DEBUG)) return; + console.group(this.colorize(this.colors.bold, `${label}`)); + } + + groupEnd() { + if (!this.shouldLog(LogLevel.DEBUG)) return; + console.groupEnd(); + } + + operation(operation: string) { + return this.child({ operation }); + } + + item(type: string, name: string) { + return this.child({ [type]: name }); + } +} diff --git a/src/types/BuilderContext.ts b/src/types/BuilderContext.ts new file mode 100644 index 0000000..d61351f --- /dev/null +++ b/src/types/BuilderContext.ts @@ -0,0 +1,26 @@ +import type { DatoBuilderConfig } from "./DatoBuilderConfig"; + +export interface BuilderContext { + /** + * The configuration object for the DatoBuilder + */ + config: Required; + + /** + * Get or create a block by name. Returns the item type ID. + * Handles caching and prevents duplicate builds automatically. + * + * @param name - The name of the block file (without extension) + * @returns Promise that resolves to the item type ID + */ + getBlock: (name: string) => Promise; + + /** + * Get or create a model by name. Returns the item type ID. + * Handles caching and prevents duplicate builds automatically. + * + * @param name - The name of the model file (without extension) + * @returns Promise that resolves to the item type ID + */ + getModel: (name: string) => Promise; +} diff --git a/src/types/DatoBuilderConfig.ts b/src/types/DatoBuilderConfig.ts new file mode 100644 index 0000000..489d506 --- /dev/null +++ b/src/types/DatoBuilderConfig.ts @@ -0,0 +1,78 @@ +import type { LogLevel } from "../logger"; + +export interface DatoBuilderConfig { + /** + * Your DatoCMS Content Management API token. + * + * You can find this in your DatoCMS project settings under β€œAPI tokens”. + * This value is required. + */ + apiToken: string; + + /** + * Whether to overwrite existing fields in DatoCMS when syncing. + * + * - `false` (default): New fields will be created and removed fields + * deleted, but any fields that already exist (matched by API key) + * will be left untouched. + * + * - `true`: Fields with matching API keys will be updated to match + * your code definitions, overwriting any manual changes made via + * the DatoCMS dashboard. + */ + overwriteExistingFields?: boolean; + + /** + * Model API Key Suffix. + * + * @default "model" + */ + modelApiKeySuffix?: string | null; + + /** + * Block API Key Suffix. + * + * @default "block" + */ + blockApiKeySuffix?: string | null; + + /** + * File-system path or glob (relative to the project root) where the CLI + * should search for model definitions. + * + * @default "./datocms/models" + */ + modelsPath?: string; + + /** + * File-system path or glob (relative to the project root) where the CLI + * should search for block definitions. + * + * @default "./datocms/blocks" + */ + blocksPath?: string; + + /** + * File-system path (relative to the project root) where synced blocks + * should be generated when running sync command. + * + * @default "./src/datocms/.generated/blocks" + */ + syncBlocksPath?: string; + + /** + * File-system path (relative to the project root) where synced models + * should be generated when running sync command. + * + * @default "./src/datocms/.generated/models" + */ + syncModelsPath?: string; + + /** + * Minimum level of messages to log. + * Higher levels suppress more verbose output. + * + * @default LogLevel.INFO + */ + logLevel?: LogLevel; +} diff --git a/src/types/ItemTypeBuilderFields.ts b/src/types/ItemTypeBuilderFields.ts new file mode 100644 index 0000000..b5abdbd --- /dev/null +++ b/src/types/ItemTypeBuilderFields.ts @@ -0,0 +1,17 @@ +import type ItemTypeBuilder from "@/ItemTypeBuilder"; + +type ExtractMethodConfig = T[K] extends ( + config: infer C, +) => any + ? C + : never; + +// Infer all addXXX methods from ItemTypeBuilder +export type ItemTypeBuilderAddMethods = { + [K in keyof ItemTypeBuilder]: K extends `add${string}` ? K : never; +}[keyof ItemTypeBuilder]; + +// Extract config type from the inferred method names +export type MethodNameToConfig = T extends ItemTypeBuilderAddMethods + ? ExtractMethodConfig + : never; diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 74d2d7b..79f7527 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,5 +1,4 @@ import type GenericDatoError from "../Api/Error/GenericDatoError"; - /** * Operation types for error messaging */ @@ -55,11 +54,17 @@ export function createUserFriendlyErrorMessage( ); // Add technical details for developers - message += `\n\nTechnical details: [${datoError.info.outerCode}/${datoError.info.innerCode || ""}] ${JSON.stringify(errorDetails)}`; + message += `\n\nTechnical details: [${datoError.info.outerCode}/${ + datoError.info.innerCode || "" + }] ${JSON.stringify(errorDetails)}`; // Add resource definition as debug info for create/update operations if ((operation === "create" || operation === "update") && resourceDef) { - message += `\n\n${resourceType} definition: ${JSON.stringify(resourceDef, null, 2)}`; + message += `\n\n${resourceType} definition: ${JSON.stringify( + resourceDef, + null, + 2, + )}`; } return message; @@ -160,7 +165,7 @@ export async function executeWithErrorHandling( resourceDef, existingResource, ); - console.error(errorMessage); - throw error; + + throw new Error(errorMessage); } } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 5397f21..c5d8617 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,14 +1,16 @@ import pluralize from "pluralize"; +interface DatoApiKeyOptions { + name: string; + suffix?: string | null; + preservePlural?: boolean; +} + export function generateDatoApiKey({ name, suffix, preservePlural = true, -}: { - name: string; - suffix?: string; - preservePlural?: boolean; -}): string { +}: DatoApiKeyOptions): string { let result = name.toLowerCase(); // Remove apostrophes (for possessive forms) @@ -50,3 +52,11 @@ export function generateDatoApiKey({ return result; } + +export function toPascalCase(str: string): string { + return str + .replace(/[^a-zA-Z0-9\s]/g, " ") + .split(/\s+/) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(""); +} diff --git a/tests/builder-fields/email.test.ts b/tests/builder-fields/email.test.ts deleted file mode 100644 index ae4f23b..0000000 --- a/tests/builder-fields/email.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { beforeEach, describe, expect, it, jest } from "@jest/globals"; -import ItemTypeBuilder from "../../src/ItemTypeBuilder"; - -jest.mock("../../src/config/loader", () => ({ - loadDatoBuilderConfig: jest.fn(() => ({ - overwriteExistingFields: false, - })), -})); - -class TestBuilder extends ItemTypeBuilder { - constructor() { - super("model", { name: "TestModel" }); - } -} - -describe("addEmail", () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it("should create a email field", () => { - const builder = new TestBuilder(); - - builder.addEmail({ - label: "Email", - body: { - api_key: "email", - }, - }); - - const fieldDefinition = builder.getField("email"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "email", - field_type: "string", - label: "Email", - position: 1, - validators: { - format: { - predefined_pattern: "email", - }, - }, - }); - }); - - it("can pass additional body properties", () => { - const builder = new TestBuilder(); - - builder.addEmail({ - label: "Email", - body: { - api_key: "email", - hint: "This is a hint", - }, - }); - - const fieldDefinition = builder.getField("email"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "email", - field_type: "string", - label: "Email", - position: 1, - hint: "This is a hint", - validators: { - format: { - predefined_pattern: "email", - }, - }, - }); - }); - - it("cannot change the predefined pattern", () => { - const builder = new TestBuilder(); - - expect(() => { - builder.addEmail({ - label: "Email", - body: { - api_key: "email", - validators: { - format: { - predefined_pattern: "url", - }, - }, - }, - }); - }).toThrow( - "The `predefined_pattern` for the format validator must be 'email' for Email fields.", - ); - }); - - it("does not throw error if the predefined pattern is email", () => { - const builder = new TestBuilder(); - - expect(() => { - builder.addEmail({ - label: "Email", - body: { - api_key: "email", - validators: { - format: { - predefined_pattern: "email", - }, - }, - }, - }); - }).not.toThrow(); - }); - - it("can pass additional validators", () => { - const builder = new TestBuilder(); - - builder.addEmail({ - label: "Email", - body: { - api_key: "email", - validators: { - required: true, - unique: true, - }, - }, - }); - - const fieldDefinition = builder.getField("email"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "email", - field_type: "string", - label: "Email", - position: 1, - validators: { - required: {}, - unique: {}, - format: { - predefined_pattern: "email", - }, - }, - }); - }); - - it("can pass custom regex", () => { - const builder = new TestBuilder(); - - builder.addEmail({ - label: "Email", - body: { - api_key: "email", - validators: { - format: { - custom_pattern: /https?:\/\/.+/i, - }, - }, - }, - }); - - const fieldDefinition = builder.getField("email"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "email", - field_type: "string", - label: "Email", - position: 1, - validators: { - format: { - custom_pattern: "/https?:\\/\\/.+/i", - }, - }, - }); - }); - - it("merge format with email predefined pattern", () => { - const builder = new TestBuilder(); - - builder.addEmail({ - label: "Email", - body: { - api_key: "email", - validators: { - format: { description: "Foo", custom_pattern: /https?:\/\/.+/i }, - }, - }, - }); - - const fieldDefinition = builder.getField("email"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "email", - field_type: "string", - label: "Email", - position: 1, - validators: { - format: { - custom_pattern: "/https?:\\/\\/.+/i", - description: "Foo", - }, - }, - }); - }); -}); diff --git a/tests/builder-fields/url.test.ts b/tests/builder-fields/url.test.ts deleted file mode 100644 index 6c79d3a..0000000 --- a/tests/builder-fields/url.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { beforeEach, describe, expect, it, jest } from "@jest/globals"; -import ItemTypeBuilder from "../../src/ItemTypeBuilder"; - -jest.mock("../../src/config/loader", () => ({ - loadDatoBuilderConfig: jest.fn(() => ({ - overwriteExistingFields: false, - })), -})); - -class TestBuilder extends ItemTypeBuilder { - constructor() { - super("model", { name: "TestModel" }); - } -} - -describe("addUrl", () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it("should create a URL field", () => { - const builder = new TestBuilder(); - - builder.addUrl({ - label: "Website", - body: { - api_key: "website_url", - }, - }); - - const fieldDefinition = builder.getField("website_url"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "website_url", - field_type: "string", - label: "Website", - position: 1, - validators: { - format: { - predefined_pattern: "url", - }, - }, - }); - }); - - it("can pass additional body properties", () => { - const builder = new TestBuilder(); - - builder.addUrl({ - label: "Website", - body: { - api_key: "website_url", - hint: "This is a hint", - }, - }); - - const fieldDefinition = builder.getField("website_url"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "website_url", - field_type: "string", - label: "Website", - position: 1, - hint: "This is a hint", - validators: { - format: { - predefined_pattern: "url", - }, - }, - }); - }); - - it("cannot change the predefined pattern", () => { - const builder = new TestBuilder(); - - expect(() => { - builder.addUrl({ - label: "Website", - body: { - api_key: "website_url", - validators: { - format: { - predefined_pattern: "email", - }, - }, - }, - }); - }).toThrow( - "The `predefined_pattern` for the format validator must be 'url' for Url fields.", - ); - }); - - it("does not throw error if the predefined pattern is url", () => { - const builder = new TestBuilder(); - - expect(() => { - builder.addUrl({ - label: "Website", - body: { - api_key: "website_url", - validators: { - format: { - predefined_pattern: "url", - }, - }, - }, - }); - }).not.toThrow(); - }); - - it("can pass additional validators", () => { - const builder = new TestBuilder(); - - builder.addUrl({ - label: "Website", - body: { - api_key: "website_url", - validators: { - required: true, - unique: true, - }, - }, - }); - - const fieldDefinition = builder.getField("website_url"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "website_url", - field_type: "string", - label: "Website", - position: 1, - validators: { - required: {}, - unique: {}, - format: { - predefined_pattern: "url", - }, - }, - }); - }); - - it("can pass custom regex", () => { - const builder = new TestBuilder(); - - builder.addUrl({ - label: "Website", - body: { - api_key: "website_url", - validators: { - format: { - custom_pattern: /https?:\/\/.+/i, - }, - }, - }, - }); - - const fieldDefinition = builder.getField("website_url"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "website_url", - field_type: "string", - label: "Website", - position: 1, - validators: { - format: { - custom_pattern: "/https?:\\/\\/.+/i", - }, - }, - }); - }); - - it("merge format with url predefined pattern", () => { - const builder = new TestBuilder(); - - builder.addUrl({ - label: "Website", - body: { - api_key: "website_url", - validators: { - format: { description: "Foo", custom_pattern: /https?:\/\/.+/i }, - }, - }, - }); - - const fieldDefinition = builder.getField("website_url"); - - expect(fieldDefinition).toBeDefined(); - expect(fieldDefinition?.body).toEqual({ - api_key: "website_url", - field_type: "string", - label: "Website", - position: 1, - validators: { - format: { - custom_pattern: "/https?:\\/\\/.+/i", - description: "Foo", - }, - }, - }); - }); -}); diff --git a/tests/client.test.ts b/tests/client.test.ts deleted file mode 100644 index a04b0ce..0000000 --- a/tests/client.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { beforeEach, describe, expect, it, jest } from "@jest/globals"; - -describe("Dato Client", () => { - beforeEach(() => { - jest.resetModules(); - }); - - it("calls buildClient with apiToken from config and returns instance", () => { - // biome-ignore lint/suspicious/noExplicitAny: any is used here to mock a client - const fakeClient = {} as any; - - // Mock loader to return desired config - jest.doMock("../src/config/loader", () => ({ - loadDatoBuilderConfig: () => ({ - apiToken: "cfg-token", - }), - })); - - // Mock datocms buildClient - const buildMock = jest.fn().mockReturnValue(fakeClient); - jest.doMock("@datocms/cma-client-node", () => ({ - buildClient: buildMock, - })); - - const { getDatoClient } = require("../src/config/index"); - const client = getDatoClient(); - - expect(buildMock).toHaveBeenCalledWith({ apiToken: "cfg-token" }); - expect(client).toBe(fakeClient); - }); -}); diff --git a/tests/config/config.test.ts b/tests/config/config.test.ts deleted file mode 100644 index 0f86313..0000000 --- a/tests/config/config.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it } from "@jest/globals"; - -describe("Config", () => { - let tmpDir: string; - let origCwd: string; - - beforeEach(() => { - origCwd = process.cwd(); - tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dato-builder-")); - process.chdir(tmpDir); - }); - - afterEach(() => { - process.chdir(origCwd); - fs.rmSync(tmpDir, { recursive: true, force: true }); - }); - - it("throws for missing apiToken", () => { - fs.writeFileSync( - path.join(tmpDir, "dato-builder.config.js"), - "module.exports = { overwriteExistingFields: false };", - ); - const { loadDatoBuilderConfig } = require("../../src/config/loader"); - expect(() => loadDatoBuilderConfig()).toThrow(/API token is required/); - }); - - it("sets overwriteExistingFields to false by default", () => { - fs.writeFileSync( - path.join(tmpDir, "dato-builder.config.js"), - "module.exports = { apiToken: 'token' };", - ); - const { loadDatoBuilderConfig } = require("../../src/config/loader"); - const config = loadDatoBuilderConfig(); - expect(config.overwriteExistingFields).toBe(false); - }); -}); diff --git a/tests/config/loader.test.ts b/tests/config/loader.test.ts deleted file mode 100644 index 6da48b3..0000000 --- a/tests/config/loader.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import { - afterEach, - beforeEach, - describe, - expect, - it, - jest, -} from "@jest/globals"; - -describe("Config Loader", () => { - let tmpDir: string; - let origCwd: string; - - beforeEach(() => { - origCwd = process.cwd(); - tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dato-builder-")); - process.chdir(tmpDir); - }); - - afterEach(() => { - process.chdir(origCwd); - fs.rmSync(tmpDir, { recursive: true, force: true }); - }); - - it("loads JS config file and applies defaults", () => { - const cfg = { apiToken: "js-token", overwriteExistingFields: true }; - fs.writeFileSync( - path.join(tmpDir, "dato-builder.config.js"), - `module.exports = ${JSON.stringify(cfg)};`, - ); - const { loadDatoBuilderConfig } = require("../../src/config/loader"); - const loaded = loadDatoBuilderConfig(); - expect(loaded.apiToken).toBe(cfg.apiToken); - expect(loaded.overwriteExistingFields).toBe(true); - }); - - it("loads TS config file without ts-node warning when ts-node installed", () => { - const cfg = { apiToken: "ts-token" }; - fs.writeFileSync( - path.join(tmpDir, "dato-builder.config.ts"), - `export default ${JSON.stringify(cfg)};`, - ); - // Simulate ts-node/register available - jest.resetModules(); - jest.doMock("ts-node/register", () => ({}), { virtual: true }); - const { loadDatoBuilderConfig } = require("../../src/config/loader"); - const loaded = loadDatoBuilderConfig(); - expect(loaded.apiToken).toBe(cfg.apiToken); - expect(loaded.overwriteExistingFields).toBe(false); - jest.dontMock("ts-node/register"); - }); - - it("throws if no config file exists", () => { - const { loadDatoBuilderConfig } = require("../../src/config/loader"); - expect(() => loadDatoBuilderConfig()).toThrow( - /No Dato Builder config found/, - ); - }); -}); diff --git a/tests/utils/mockCache.ts b/tests/utils/mockCache.ts new file mode 100644 index 0000000..3617a1c --- /dev/null +++ b/tests/utils/mockCache.ts @@ -0,0 +1,18 @@ +import { jest } from "@jest/globals"; +import type { CacheManager } from "../../src/cache/CacheManager"; + +export function createMockCache(): jest.Mocked { + const mock = { + get: jest.fn(), + set: jest.fn(), + delete: jest.fn(), + clear: jest.fn(), + has: jest.fn(), + keys: jest.fn(), + values: jest.fn(), + entries: jest.fn(), + size: jest.fn().mockReturnValue(0), + } as unknown as jest.Mocked; + + return mock; +} diff --git a/tests/utils/mockConfig.ts b/tests/utils/mockConfig.ts new file mode 100644 index 0000000..ff8fe1b --- /dev/null +++ b/tests/utils/mockConfig.ts @@ -0,0 +1,23 @@ +import { LogLevel } from "@/logger"; +import type { DatoBuilderConfig } from "../../src"; + +export function createMockConfig( + config?: Partial, +): Required { + const defaultMockedConfig: Required = { + apiToken: "custom-token", + overwriteExistingFields: false, + modelApiKeySuffix: "custom-model", + blockApiKeySuffix: "custom-block", + blocksPath: "/custom/blocks", + modelsPath: "/custom/models", + logLevel: LogLevel.NONE, + syncBlocksPath: "/custom/sync/blocks", + syncModelsPath: "/custom/sync/models", + }; + + return { + ...defaultMockedConfig, + ...config, + }; +} diff --git a/tests/utils/mockLogger.ts b/tests/utils/mockLogger.ts new file mode 100644 index 0000000..c0691b9 --- /dev/null +++ b/tests/utils/mockLogger.ts @@ -0,0 +1,31 @@ +import { jest } from "@jest/globals"; +import type { ConsoleLogger } from "@/logger"; + +export function createMockLogger(): jest.Mocked { + const mock = { + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + trace: jest.fn(), + success: jest.fn(), + json: jest.fn(), + debugJson: jest.fn(), + infoJson: jest.fn(), + errorJson: jest.fn(), + traceJson: jest.fn(), + time: jest.fn(), + timeEnd: jest.fn().mockReturnValue(undefined), + progress: jest.fn(), + banner: jest.fn(), + table: jest.fn(), + group: jest.fn(), + groupEnd: jest.fn(), + setLevel: jest.fn(), + operation: jest.fn().mockReturnThis(), // returns the logger itself + item: jest.fn().mockReturnThis(), + child: jest.fn().mockImplementation(() => mock), // allows chaining + } as unknown as jest.Mocked; + + return mock; +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..99ad800 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,50 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "node_modules", + "bin", + "build", + "coverage", + "tests", + "**/*.test.ts", + "*.js", + "*.mjs", + "*.ts" + ], + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "declaration": true, + "emitDeclarationOnly": false, + "lib": ["es2020"], + "allowJs": true, + "outDir": "build", + "rootDir": "src", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "strict": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "exactOptionalPropertyTypes": false, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": false, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true, + "noFallthroughCasesInSwitch": true, + "isolatedModules": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "tsBuildInfoFile": "./build/.tsbuildinfo", + "removeComments": true, + "sourceMap": false + }, + "include": ["src/**/*"] +} diff --git a/tsconfig.json b/tsconfig.json index f43da88..34f90ca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,23 +4,43 @@ "bin", "build", "coverage", - "tests", "*.js", "*.mjs", "*.ts" ], "compilerOptions": { - "target": "es5", + "target": "es2020", "module": "commonjs", "declaration": true, "emitDeclarationOnly": false, - "lib": ["es6"], + "lib": ["es2020"], "allowJs": true, "outDir": "build", - "rootDir": "src", + "rootDir": ".", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"], + "@tests/*": ["tests/*"] + }, "strict": true, "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "exactOptionalPropertyTypes": false, + "noImplicitOverride": true, + "noFallthroughCasesInSwitch": true, + "isolatedModules": true, + "moduleResolution": "node", + "noPropertyAccessFromIndexSignature": false, + "noUncheckedIndexedAccess": true, "esModuleInterop": true, - "resolveJsonModule": true - } + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "tsBuildInfoFile": "./build/.tsbuildinfo" + }, + "include": ["src/**/*", "tests/**/*"] }