From 1d6d512b71bd42409df6ed71216a85c6a0ae3daa Mon Sep 17 00:00:00 2001 From: Matt Galligan Date: Mon, 1 Sep 2025 14:02:57 -0400 Subject: [PATCH 1/5] feat: replace ESLint with Biome and add Lefthook for git hooks - Replace ESLint with Biome/Ultracite for TS/JS linting and formatting - Add Lefthook for managing git hooks - Configure markdownlint-cli2 for Markdown formatting - Update all package.json scripts to use new tooling - Update CI workflow to use new linting commands - Remove old ESLint configuration files --- .biomeignore | 8 + .changeset/config.json | 2 +- .eslintrc.js | 23 - .github/actions/setup-bun-and-deps/action.yml | 33 +- .markdownlint-cli2.yaml | 23 + .markdownlint.json | 2 +- .prettierignore | 19 + .prettierrc | 16 +- biome.json | 43 ++ bun.lock | 405 +++++++----------- lefthook.yml | 23 + package.json | 31 +- packages/core/package.json | 8 +- .../src/compiler/__tests__/compiler.spec.ts | 102 ++--- packages/core/src/compiler/index.ts | 40 +- .../__tests__/cursor-plugin.spec.ts | 117 +++-- .../__tests__/windsurf-plugin.spec.ts | 107 ++--- .../core/src/destinations/cursor-plugin.ts | 30 +- packages/core/src/destinations/index.ts | 13 +- .../core/src/destinations/windsurf-plugin.ts | 32 +- packages/core/src/index.ts | 68 +-- packages/core/src/interfaces/compiled-doc.ts | 44 +- .../core/src/interfaces/destination-plugin.ts | 10 +- packages/core/src/interfaces/index.ts | 7 +- packages/core/src/interfaces/logger.ts | 23 +- .../core/src/linter/__tests__/linter.spec.ts | 103 ++--- packages/core/src/linter/index.ts | 68 ++- .../core/src/parser/__tests__/parser.spec.ts | 41 +- packages/core/src/parser/index.ts | 57 +-- packages/core/tests/integration/e2e.spec.ts | 132 +++--- packages/core/tsconfig.json | 2 +- packages/core/tsup.config.ts | 12 +- packages/core/vitest.config.ts | 19 +- tsconfig.base.json | 2 +- turbo.json | 2 +- 35 files changed, 849 insertions(+), 818 deletions(-) create mode 100644 .biomeignore delete mode 100644 .eslintrc.js create mode 100644 .markdownlint-cli2.yaml create mode 100644 .prettierignore create mode 100644 biome.json create mode 100644 lefthook.yml diff --git a/.biomeignore b/.biomeignore new file mode 100644 index 0000000..6041457 --- /dev/null +++ b/.biomeignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +.turbo/ +coverage/ +*.lock +bun.lock +.changeset/ +docs_archive/ \ No newline at end of file diff --git a/.changeset/config.json b/.changeset/config.json index b867eed..fce1c26 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -8,4 +8,4 @@ "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [] -} \ No newline at end of file +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 99c30e0..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,23 +0,0 @@ -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'prettier', - ], - env: { - node: true, - es2021: true, - }, - parserOptions: { - ecmaVersion: 2021, - sourceType: 'module', - }, - ignorePatterns: ['dist', 'node_modules', 'coverage', '*.js'], - rules: { - '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], - }, -}; \ No newline at end of file diff --git a/.github/actions/setup-bun-and-deps/action.yml b/.github/actions/setup-bun-and-deps/action.yml index 060e0da..fdc80ab 100644 --- a/.github/actions/setup-bun-and-deps/action.yml +++ b/.github/actions/setup-bun-and-deps/action.yml @@ -1,26 +1,26 @@ -name: 'Setup Bun and Dependencies' -description: 'Setup Bun runtime, install dependencies, and optionally run lint/test/build' +name: "Setup Bun and Dependencies" +description: "Setup Bun runtime, install dependencies, and optionally run lint/test/build" inputs: bun-version: - description: 'Version of Bun to install' + description: "Version of Bun to install" required: true - default: '1.2.21' + default: "1.2.21" run-lint: - description: 'Whether to run lint' + description: "Whether to run lint" required: false - default: 'true' + default: "true" run-test: - description: 'Whether to run test' + description: "Whether to run test" required: false - default: 'true' + default: "true" run-build: - description: 'Whether to run build' + description: "Whether to run build" required: false - default: 'true' + default: "true" runs: - using: 'composite' + using: "composite" steps: - name: Setup Bun uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2 @@ -31,9 +31,14 @@ runs: run: bun install --frozen-lockfile shell: bash - - name: Lint + - name: Check Format if: inputs.run-lint == 'true' - run: bun run lint + run: bun run check:ci + shell: bash + + - name: Lint Markdown + if: inputs.run-lint == 'true' + run: bun run lint:md shell: bash - name: Test @@ -44,4 +49,4 @@ runs: - name: Build if: inputs.run-build == 'true' run: bun run build - shell: bash \ No newline at end of file + shell: bash diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 0000000..21e9918 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,23 @@ +config: + default: true + MD013: false # Line length + MD033: false # Allow inline HTML + MD041: false # First line in a file should be a top-level heading + MD024: + siblings_only: true # Allow duplicate headings if not siblings + MD025: false # Multiple top-level headings + line-length: false + +ignores: + - node_modules/** + - "**/node_modules/**" + - dist/** + - "**/dist/**" + - .turbo/** + - "**/.turbo/**" + - coverage/** + - "**/coverage/**" + - "**/*.lock" + - "**/bun.lock" + - .changeset/** + - docs_archive/** \ No newline at end of file diff --git a/.markdownlint.json b/.markdownlint.json index bf6b366..1dd8622 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -3,4 +3,4 @@ "MD013": false, "MD033": false, "MD041": false -} \ No newline at end of file +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..fab91f7 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,19 @@ +# Prettier should only handle YAML and Markdown files +# TypeScript/JavaScript files are handled by Biome/Ultracite + +*.ts +*.tsx +*.js +*.jsx +*.json +*.jsonc + +# Standard ignores +node_modules/ +dist/ +build/ +coverage/ +.turbo/ +*.lock +bun.lock +.changeset/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index e62605e..b777b6b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,9 +1,19 @@ { "semi": true, "singleQuote": true, - "trailingComma": "all", - "printWidth": 100, "tabWidth": 2, "useTabs": false, - "endOfLine": "lf" + "trailingComma": "es5", + "bracketSpacing": true, + "arrowParens": "always", + "printWidth": 100, + "endOfLine": "lf", + "overrides": [ + { + "files": ["*.yml", "*.yaml"], + "options": { + "singleQuote": false + } + } + ] } \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..1717178 --- /dev/null +++ b/biome.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.2/schema.json", + "extends": ["ultracite"], + "javascript": { + "globals": ["Bun"] + }, + "json": { + "parser": { + "allowComments": true, + "allowTrailingCommas": true + } + }, + "files": { + "ignoreUnknown": true, + "includes": ["**/*.{js,ts,jsx,tsx,json,jsonc}"] + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "linter": { + "rules": { + "style": { + "noParameterAssign": "off", + "noNonNullAssertion": "off" + }, + "complexity": { + "noBannedTypes": "off", + "noExcessiveCognitiveComplexity": "off" + }, + "suspicious": { + "noExplicitAny": "off", + "noArrayIndexKey": "off" + } + } + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + } +} diff --git a/bun.lock b/bun.lock index e961fec..cdabe27 100644 --- a/bun.lock +++ b/bun.lock @@ -4,16 +4,16 @@ "": { "name": "rulesets", "devDependencies": { + "@biomejs/biome": "^2.2.0", "@changesets/cli": "^2.29.6", + "@types/bun": "latest", "@types/node": "^24.3.0", - "@typescript-eslint/eslint-plugin": "^8.41.0", - "@typescript-eslint/parser": "^8.41.0", - "eslint": "^9.34.0", - "eslint-config-prettier": "^10.1.8", - "markdownlint-cli": "^0.45.0", + "lefthook": "^1.12.3", + "markdownlint-cli2": "^0.18.1", "prettier": "^3.6.2", "turbo": "^2.5.6", "typescript": "^5.9.2", + "ultracite": "^5.1.5", }, }, "packages/core": { @@ -26,7 +26,6 @@ "@types/js-yaml": "^4.0.9", "@types/json-schema": "^7.0.15", "@types/node": "^18.19.31", - "eslint": "^8.57.0", "tsup": "^8.0.2", "typescript": "^5.4.5", "vitest": "^1.6.0", @@ -36,6 +35,24 @@ "packages": { "@babel/runtime": ["@babel/runtime@7.28.3", "", {}, "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA=="], + "@biomejs/biome": ["@biomejs/biome@2.2.2", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.2", "@biomejs/cli-darwin-x64": "2.2.2", "@biomejs/cli-linux-arm64": "2.2.2", "@biomejs/cli-linux-arm64-musl": "2.2.2", "@biomejs/cli-linux-x64": "2.2.2", "@biomejs/cli-linux-x64-musl": "2.2.2", "@biomejs/cli-win32-arm64": "2.2.2", "@biomejs/cli-win32-x64": "2.2.2" }, "bin": { "biome": "bin/biome" } }, "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg=="], + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.0.12", "", { "dependencies": { "@changesets/config": "^3.1.1", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ=="], "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.9", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ=="], @@ -70,6 +87,10 @@ "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="], + "@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="], + + "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="], @@ -122,42 +143,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="], - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], - - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], - - "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], - - "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="], - - "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="], - - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], - - "@eslint/js": ["@eslint/js@9.34.0", "", {}, "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw=="], - - "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], - - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="], - - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], - - "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], - - "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="], - - "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], - - "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="], - - "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - "@inquirer/external-editor": ["@inquirer/external-editor@1.0.1", "", { "dependencies": { "chardet": "^2.1.0", "iconv-lite": "^0.6.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q=="], - "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], - - "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], - "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "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" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], @@ -228,8 +215,18 @@ "@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="], + + "@trpc/server": ["@trpc/server@11.5.0", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-0IBtkmUCeO2ycn4K45/cqsujnlCQrSvkCo7lFDpg3kGMIPiLyLRciID5IiS7prEjRjeITa+od2aaHTIwONApVw=="], + + "@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="], + + "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="], @@ -242,31 +239,17 @@ "@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], - "@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], - - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.41.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/type-utils": "8.41.0", "@typescript-eslint/utils": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.41.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw=="], - - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.41.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg=="], - - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.41.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.41.0", "@typescript-eslint/types": "^8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ=="], - - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0" } }, "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ=="], + "@types/omelette": ["@types/omelette@0.4.5", "", {}, "sha512-zUCJpVRwfMcZfkxSCGp73mgd3/xesvPz5tQJIORlfP/zkYEyp9KUfF7IP3RRjyZR3DwxkPs96/IFf70GmYZYHQ=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.41.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw=="], + "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="], - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ=="], - - "@typescript-eslint/types": ["@typescript-eslint/types@8.41.0", "", {}, "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag=="], - - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.41.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.41.0", "@typescript-eslint/tsconfig-utils": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ=="], - - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.41.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A=="], + "@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg=="], + "@vitest/expect": ["@vitest/expect@1.6.1", "", { "dependencies": { "@vitest/spy": "1.6.1", "@vitest/utils": "1.6.1", "chai": "^4.3.10" } }, "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], - "@vitest/expect": ["@vitest/expect@1.6.1", "", { "dependencies": { "@vitest/spy": "1.6.1", "@vitest/utils": "1.6.1", "chai": "^4.3.10" } }, "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog=="], + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], "@vitest/runner": ["@vitest/runner@1.6.1", "", { "dependencies": { "@vitest/utils": "1.6.1", "p-limit": "^5.0.0", "pathe": "^1.1.1" } }, "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA=="], @@ -278,17 +261,13 @@ "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], @@ -302,20 +281,18 @@ "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="], + "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "chai": ["chai@4.5.0", "", { "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.1.0" } }, "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw=="], - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], @@ -330,29 +307,29 @@ "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], - "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="], "deep-eql": ["deep-eql@4.1.4", "", { "dependencies": { "type-detect": "^4.0.0" } }, "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg=="], - "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], - - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], @@ -364,8 +341,6 @@ "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], - "doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], - "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -374,86 +349,52 @@ "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], - "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="], - - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - - "eslint": ["eslint@9.34.0", "", { "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.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@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" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg=="], + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], - "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], - - "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], - - "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], - - "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="], "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], - - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], - - "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - "execa": ["execa@8.0.1", "", { "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" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], - "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], - "fast-glob": ["fast-glob@3.3.3", "", { "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" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], - "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + "fast-glob": ["fast-glob@3.3.3", "", { "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" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], "fix-dts-default-cjs-exports": ["fix-dts-default-cjs-exports@1.0.1", "", { "dependencies": { "magic-string": "^0.30.17", "mlly": "^1.7.4", "rollup": "^4.34.8" } }, "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg=="], - "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - - "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "get-func-name": ["get-func-name@2.0.2", "", {}, "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="], "get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="], - "glob": ["glob@11.0.3", "", { "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" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="], - - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + "glob": ["glob@10.4.5", "", { "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" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], - "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + "globby": ["globby@14.1.0", "", { "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" } }, "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], - - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - "human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="], "human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], @@ -462,16 +403,6 @@ "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - - "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - - "ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="], - "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], @@ -488,8 +419,6 @@ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], - "is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="], "is-subdir": ["is-subdir@1.2.0", "", { "dependencies": { "better-path-resolve": "1.0.0" } }, "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw=="], @@ -498,7 +427,7 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="], + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], @@ -506,23 +435,33 @@ "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], - "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], - "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + "katex": ["katex@0.16.22", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg=="], - "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + "lefthook": ["lefthook@1.12.3", "", { "optionalDependencies": { "lefthook-darwin-arm64": "1.12.3", "lefthook-darwin-x64": "1.12.3", "lefthook-freebsd-arm64": "1.12.3", "lefthook-freebsd-x64": "1.12.3", "lefthook-linux-arm64": "1.12.3", "lefthook-linux-x64": "1.12.3", "lefthook-openbsd-arm64": "1.12.3", "lefthook-openbsd-x64": "1.12.3", "lefthook-windows-arm64": "1.12.3", "lefthook-windows-x64": "1.12.3" }, "bin": { "lefthook": "bin/index.js" } }, "sha512-huMg+mGp6wHPjkaLdchuOvxVRMzvz6OVdhivatiH2Qn47O5Zm46jwzbVPYIanX6N/8ZTjGLBxv8tZ0KYmKt/Jg=="], - "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + "lefthook-darwin-arm64": ["lefthook-darwin-arm64@1.12.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-j1lwaosWRy3vhz8oQgCS1M6EUFN95aIYeNuqkczsBoAA6BDNAmVP1ctYEIYUK4bYaIgENbqbA9prYMAhyzh6Og=="], - "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], + "lefthook-darwin-x64": ["lefthook-darwin-x64@1.12.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-x6aWFfLQX4m5zQ4X9zh5+hHOE5XTvNjz2zB9DI+xbIBLs2RRg0xJNT3OfgSrBU1QtEBneJ5dRQP5nl47td9GDQ=="], - "katex": ["katex@0.16.22", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg=="], + "lefthook-freebsd-arm64": ["lefthook-freebsd-arm64@1.12.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-41OmulLqVZ0EOHmmHouJrpL59SwDD7FLoso4RsQVIBPaf8fHacdLo07Ye28VWQ5XolZQvnWcr1YXKo4JhqQMyw=="], + + "lefthook-freebsd-x64": ["lefthook-freebsd-x64@1.12.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-741/JRCJIS++hgYEH2uefN4FsH872V7gy2zDhcfQofiZnWP7+qhl4Wmwi8IpjIu4X7hLOC4cT18LOVU5L8KV9Q=="], + + "lefthook-linux-arm64": ["lefthook-linux-arm64@1.12.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-BXIy1aDFZmFgmebJliNrEqZfX1lSOD4b/USvANv1UirFrNgTq5SRssd1CKfflT2PwKX6LsJTD4WabLLWZOxp9A=="], + + "lefthook-linux-x64": ["lefthook-linux-x64@1.12.3", "", { "os": "linux", "cpu": "x64" }, "sha512-FRdwdj5jsQAP2eVrtkVUqMqYNCbQ2Ix84izy29/BvLlu/hVypAGbDfUkgFnsmAd6ZsCBeYCEtPuqyg3E3SO0Rg=="], + + "lefthook-openbsd-arm64": ["lefthook-openbsd-arm64@1.12.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-tch5wXY4GOjKAYohH7OFoxNdVHuUSYt2Pulo2VTkMYEG8IrvJnRO5MkvgHtKDHzU5mfABQYv5+ccJykDx5hQWA=="], + + "lefthook-openbsd-x64": ["lefthook-openbsd-x64@1.12.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-IHbHg/rUFXrAN7LnjcQEtutCHBaD49CZge96Hpk0GZ2eEG5GTCNRnUyEf+Kf3+RTqHFgwtADdpeDa/ZaGZTM4g=="], - "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "lefthook-windows-arm64": ["lefthook-windows-arm64@1.12.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-wghcE5TSpb+mbtemUV6uAo9hEK09kxRzhf2nPdeDX+fw42cL2TGZsbaCnDyzaY144C+L2/wEWrLIHJMnZYkuqA=="], - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + "lefthook-windows-x64": ["lefthook-windows-x64@1.12.3", "", { "os": "win32", "cpu": "x64" }, "sha512-7Co/L8e2x2hGC1L33jDJ4ZlTkO3PJm25GOGpLfN1kqwhGB/uzMLeTI/PBczjlIN8isUv26ouNd9rVR7Bibrwyg=="], "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], @@ -534,9 +473,7 @@ "local-pkg": ["local-pkg@0.5.1", "", { "dependencies": { "mlly": "^1.7.3", "pkg-types": "^1.2.1" } }, "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ=="], - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], "lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="], @@ -544,7 +481,7 @@ "loupe": ["loupe@2.3.7", "", { "dependencies": { "get-func-name": "^2.0.1" } }, "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA=="], - "lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "magic-string": ["magic-string@0.30.18", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ=="], @@ -552,7 +489,9 @@ "markdownlint": ["markdownlint@0.38.0", "", { "dependencies": { "micromark": "4.0.2", "micromark-core-commonmark": "2.0.3", "micromark-extension-directive": "4.0.0", "micromark-extension-gfm-autolink-literal": "2.1.0", "micromark-extension-gfm-footnote": "2.1.0", "micromark-extension-gfm-table": "2.1.1", "micromark-extension-math": "3.1.0", "micromark-util-types": "2.0.2" } }, "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ=="], - "markdownlint-cli": ["markdownlint-cli@0.45.0", "", { "dependencies": { "commander": "~13.1.0", "glob": "~11.0.2", "ignore": "~7.0.4", "js-yaml": "~4.1.0", "jsonc-parser": "~3.3.1", "jsonpointer": "~5.0.1", "markdown-it": "~14.1.0", "markdownlint": "~0.38.0", "minimatch": "~10.0.1", "run-con": "~1.3.2", "smol-toml": "~1.3.4" }, "bin": { "markdownlint": "markdownlint.js" } }, "sha512-GiWr7GfJLVfcopL3t3pLumXCYs8sgWppjIA1F/Cc3zIMgD3tmkpyZ1xkm1Tej8mw53B93JsDjgA3KOftuYcfOw=="], + "markdownlint-cli2": ["markdownlint-cli2@0.18.1", "", { "dependencies": { "globby": "14.1.0", "js-yaml": "4.1.0", "jsonc-parser": "3.3.1", "markdown-it": "14.1.0", "markdownlint": "0.38.0", "markdownlint-cli2-formatter-default": "0.0.5", "micromatch": "4.0.8" }, "bin": { "markdownlint-cli2": "markdownlint-cli2-bin.mjs" } }, "sha512-/4Osri9QFGCZOCTkfA8qJF+XGjKYERSHkXzxSyS1hd3ZERJGjvsUao2h4wdnvpHp6Tu2Jh/bPHM0FE9JJza6ng=="], + + "markdownlint-cli2-formatter-default": ["markdownlint-cli2-formatter-default@0.0.5", "", { "peerDependencies": { "markdownlint-cli2": ">=0.0.4" } }, "sha512-4XKTwQ5m1+Txo2kuQ3Jgpo/KmnG+X90dWt4acufg6HVGadTUG5hzHF/wssp9b5MBYOMCnZ9RMPaU//uHsszF8Q=="], "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], @@ -614,9 +553,7 @@ "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], @@ -630,25 +567,21 @@ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - "npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "nypm": ["nypm@0.6.1", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.2.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w=="], - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "^2.0.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="], "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="], @@ -658,19 +591,15 @@ "package-manager-detector": ["package-manager-detector@0.2.11", "", { "dependencies": { "quansync": "^0.2.7" } }, "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ=="], - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + "path-type": ["path-type@6.0.0", "", {}, "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ=="], "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], @@ -678,20 +607,18 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], - "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], - "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], - "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -714,12 +641,8 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], - "rollup": ["rollup@4.50.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.0", "@rollup/rollup-android-arm64": "4.50.0", "@rollup/rollup-darwin-arm64": "4.50.0", "@rollup/rollup-darwin-x64": "4.50.0", "@rollup/rollup-freebsd-arm64": "4.50.0", "@rollup/rollup-freebsd-x64": "4.50.0", "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", "@rollup/rollup-linux-arm-musleabihf": "4.50.0", "@rollup/rollup-linux-arm64-gnu": "4.50.0", "@rollup/rollup-linux-arm64-musl": "4.50.0", "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", "@rollup/rollup-linux-ppc64-gnu": "4.50.0", "@rollup/rollup-linux-riscv64-gnu": "4.50.0", "@rollup/rollup-linux-riscv64-musl": "4.50.0", "@rollup/rollup-linux-s390x-gnu": "4.50.0", "@rollup/rollup-linux-x64-gnu": "4.50.0", "@rollup/rollup-linux-x64-musl": "4.50.0", "@rollup/rollup-openharmony-arm64": "4.50.0", "@rollup/rollup-win32-arm64-msvc": "4.50.0", "@rollup/rollup-win32-ia32-msvc": "4.50.0", "@rollup/rollup-win32-x64-msvc": "4.50.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw=="], - "run-con": ["run-con@1.3.2", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~4.1.0", "minimist": "^1.2.8", "strip-json-comments": "~3.1.1" }, "bin": { "run-con": "cli.js" } }, "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg=="], - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], @@ -734,9 +657,9 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], - "smol-toml": ["smol-toml@1.3.4", "", {}, "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA=="], + "slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], "source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="], @@ -762,18 +685,12 @@ "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "strip-literal": ["strip-literal@2.1.1", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q=="], "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="], - "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], - "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], @@ -786,6 +703,8 @@ "tinypool": ["tinypool@0.8.4", "", {}, "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ=="], + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + "tinyspy": ["tinyspy@2.2.1", "", {}, "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -794,7 +713,7 @@ "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], - "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + "trpc-cli": ["trpc-cli@0.10.2", "", { "dependencies": { "@trpc/server": "^11.1.1", "@types/omelette": "^0.4.4", "commander": "^14.0.0", "picocolors": "^1.0.1", "zod": "^3.25.3", "zod-to-json-schema": "^3.23.0" }, "peerDependencies": { "@inquirer/prompts": "*", "omelette": "*" }, "optionalPeers": ["@inquirer/prompts", "omelette"], "bin": { "trpc-cli": "dist/bin.js" } }, "sha512-zBkL88AeX0vQLXwEAcX6WUoT4Sopr97nFDFeD1zmW33wHQwBKbszylplNVk6BO/cuhgm/iq8/cG27NokqKA1mw=="], "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], @@ -814,23 +733,21 @@ "turbo-windows-arm64": ["turbo-windows-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q=="], - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "type-detect": ["type-detect@4.1.0", "", {}, "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw=="], - "type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], - "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + "ultracite": ["ultracite@5.3.0", "", { "dependencies": { "@clack/prompts": "^0.11.0", "deepmerge": "^4.3.1", "jsonc-parser": "^3.3.1", "nypm": "^0.6.1", "trpc-cli": "^0.10.2", "vitest": "^3.2.4", "zod": "^4.1.5" }, "bin": { "ultracite": "dist/index.js" } }, "sha512-h9EXOPoGTFOVhNALW+BI1qJ11R31mvtn+xFzn/CZ/xPBgdqfKT4BE1ZkmL+V3MNRnWDgNdNZC/fqOiIKFmvl7A=="], + "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], - "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], "vite": ["vite@5.4.19", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="], @@ -846,87 +763,73 @@ "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], + "zod": ["zod@4.1.5", "", {}, "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="], + "@changesets/apply-release-plan/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], "@changesets/parse/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], - "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - - "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], - - "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - - "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], - "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - "@manypkg/find-root/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], "@manypkg/get-packages/@changesets/types": ["@changesets/types@4.1.0", "", {}, "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw=="], "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], - "@rulesets/core/@types/node": ["@types/node@18.19.123", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg=="], + "@manypkg/get-packages/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], - "@rulesets/core/eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="], + "@rulesets/core/@types/node": ["@types/node@18.19.123", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg=="], - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@vitest/mocker/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], "@vitest/runner/p-limit": ["p-limit@5.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ=="], - "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "dir-glob/path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], - "glob/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], - - "globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - - "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], - "markdownlint-cli/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], - - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "local-pkg/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], - "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + "nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "nypm/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], - "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "read-yaml-file/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], - "rimraf/glob": ["glob@7.2.3", "", { "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" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - "sucrase/glob": ["glob@10.4.5", "", { "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" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "trpc-cli/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "ultracite/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], "vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], @@ -934,43 +837,53 @@ "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "zod-to-json-schema/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@changesets/parse/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], - "@manypkg/find-root/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + "@manypkg/get-packages/globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "@manypkg/get-packages/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "@rulesets/core/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@rulesets/core/eslint/@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], + "@vitest/mocker/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], + + "local-pkg/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], - "@rulesets/core/eslint/@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="], + "local-pkg/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "@rulesets/core/eslint/eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], + "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], - "@rulesets/core/eslint/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "@rulesets/core/eslint/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], - "@rulesets/core/eslint/file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], + "ultracite/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - "@rulesets/core/eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "ultracite/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "ultracite/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "ultracite/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "ultracite/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "ultracite/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + + "ultracite/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "sucrase/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + "ultracite/vitest/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "ultracite/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - "sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "ultracite/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], @@ -1022,12 +935,20 @@ "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], - "@manypkg/find-root/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "ultracite/vitest/@vitest/runner/strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="], + + "ultracite/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], + + "ultracite/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "ultracite/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "ultracite/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - "@rulesets/core/eslint/file-entry-cache/flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], + "ultracite/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - "sucrase/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "ultracite/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - "sucrase/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "ultracite/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], } } diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..f434186 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,23 @@ +pre-commit: + parallel: true + commands: + ultracite: + glob: "**/*.{ts,tsx,js,jsx,json}" + run: "bunx ultracite format {staged_files}" + prettier: + glob: "**/*.{md,yml,yaml}" + run: "bunx prettier --write {staged_files}" + markdownlint: + glob: "**/*.md" + run: "bunx markdownlint-cli2 {staged_files} --fix" + +pre-push: + parallel: true + commands: + build: + run: "bun run build" + typecheck: + run: "bun run typecheck" + test: + tags: [optional] + run: "bun run test" \ No newline at end of file diff --git a/package.json b/package.json index 4a8586c..36de34f 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,22 @@ "dev": "turbo dev", "build": "turbo build", "test": "turbo test", - "lint": "turbo lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "lint": "bunx ultracite lint .", + "lint:fix": "bunx ultracite format .", + "lint:md": "markdownlint-cli2 .", + "format": "bunx ultracite format .", + "format:check": "bunx ultracite lint", + "format:fix": "bunx ultracite format .", + "format:md": "markdownlint-cli2 . --fix", + "typecheck": "turbo typecheck", + "check": "bunx ultracite format .", + "check:ci": "bunx ultracite lint .", + "check:all": "bun run format && bun run lint && bun run lint:md && bun run typecheck", + "ci": "bun run format && bun run lint && bun run lint:md && bun run typecheck && bun test", "changeset": "changeset", "version-packages": "changeset version", - "release": "turbo build --filter=@rulesets/core && changeset publish" + "release": "turbo build --filter=@rulesets/core && changeset publish", + "prepare": "lefthook install" }, "author": "Maybe Good Systems", "license": "MIT", @@ -23,15 +34,15 @@ "node": ">=18.0.0" }, "devDependencies": { + "@biomejs/biome": "^2.2.0", "@changesets/cli": "^2.29.6", + "@types/bun": "latest", "@types/node": "^24.3.0", - "@typescript-eslint/eslint-plugin": "^8.41.0", - "@typescript-eslint/parser": "^8.41.0", - "eslint": "^9.34.0", - "eslint-config-prettier": "^10.1.8", - "markdownlint-cli": "^0.45.0", + "lefthook": "^1.12.3", + "markdownlint-cli2": "^0.18.1", "prettier": "^3.6.2", "turbo": "^2.5.6", - "typescript": "^5.9.2" + "typescript": "^5.9.2", + "ultracite": "^5.1.5" } -} \ No newline at end of file +} diff --git a/packages/core/package.json b/packages/core/package.json index f8e01a3..d05929b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,7 +21,8 @@ "dev": "tsup --watch", "test": "vitest run", "test:watch": "vitest watch", - "lint": "eslint . --ext .ts,.tsx", + "lint": "bunx ultracite lint .", + "lint:fix": "bunx ultracite format .", "typecheck": "tsc --noEmit" }, "dependencies": { @@ -33,11 +34,10 @@ "@types/json-schema": "^7.0.15", "tsup": "^8.0.2", "typescript": "^5.4.5", - "vitest": "^1.6.0", - "eslint": "^8.57.0" + "vitest": "^1.6.0" }, "publishConfig": { "access": "public" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/core/src/compiler/__tests__/compiler.spec.ts b/packages/core/src/compiler/__tests__/compiler.spec.ts index 9ed096c..0a1a423 100644 --- a/packages/core/src/compiler/__tests__/compiler.spec.ts +++ b/packages/core/src/compiler/__tests__/compiler.spec.ts @@ -1,10 +1,10 @@ -import { describe, it, expect } from 'vitest'; -import { compile } from '../index'; -import type { ParsedDoc } from '../../interfaces'; +import { describe, expect, it } from "vitest"; +import type { ParsedDoc } from "../../interfaces"; +import { compile } from "../index"; -describe('compiler', () => { - describe('compile', () => { - it('should compile a document with frontmatter and body', () => { +describe("compiler", () => { + describe("compile", () => { + it("should compile a document with frontmatter and body", () => { const parsedDoc: ParsedDoc = { source: { content: `--- @@ -21,13 +21,13 @@ destinations: This is the body with {{stems}} and {{$variables}}.`, frontmatter: { - mixdown: 'v0', - title: 'Test Rules', - description: 'Test description', + mixdown: "v0", + title: "Test Rules", + description: "Test description", destinations: { cursor: { - outputPath: '.cursor/rules/test.mdc', - priority: 'high', + outputPath: ".cursor/rules/test.mdc", + priority: "high", }, }, }, @@ -40,7 +40,7 @@ This is the body with {{stems}} and {{$variables}}.`, }, }; - const result = compile(parsedDoc, 'cursor'); + const result = compile(parsedDoc, "cursor"); // Source should be preserved expect(result.source).toEqual(parsedDoc.source); @@ -49,29 +49,31 @@ This is the body with {{stems}} and {{$variables}}.`, expect(result.ast).toEqual(parsedDoc.ast); // Output should contain only the body - expect(result.output.content).toBe('# Test Content\n\nThis is the body with {{stems}} and {{$variables}}.'); + expect(result.output.content).toBe( + "# Test Content\n\nThis is the body with {{stems}} and {{$variables}}." + ); // Metadata should include relevant fields expect(result.output.metadata).toEqual({ - title: 'Test Rules', - description: 'Test description', + title: "Test Rules", + description: "Test description", version: undefined, - outputPath: '.cursor/rules/test.mdc', - priority: 'high', + outputPath: ".cursor/rules/test.mdc", + priority: "high", }); // Context should include destination and config - expect(result.context.destinationId).toBe('cursor'); + expect(result.context.destinationId).toBe("cursor"); expect(result.context.config).toEqual({ - outputPath: '.cursor/rules/test.mdc', - priority: 'high', + outputPath: ".cursor/rules/test.mdc", + priority: "high", }); }); - it('should handle document without frontmatter', () => { + it("should handle document without frontmatter", () => { const parsedDoc: ParsedDoc = { source: { - content: '# Just Content\n\nNo frontmatter here.', + content: "# Just Content\n\nNo frontmatter here.", }, ast: { stems: [], @@ -81,18 +83,18 @@ This is the body with {{stems}} and {{$variables}}.`, }, }; - const result = compile(parsedDoc, 'windsurf'); + const result = compile(parsedDoc, "windsurf"); - expect(result.output.content).toBe('# Just Content\n\nNo frontmatter here.'); + expect(result.output.content).toBe("# Just Content\n\nNo frontmatter here."); expect(result.output.metadata).toEqual({ title: undefined, description: undefined, version: undefined, }); - expect(result.context.destinationId).toBe('windsurf'); + expect(result.context.destinationId).toBe("windsurf"); }); - it('should merge project config with destination config', () => { + it("should merge project config with destination config", () => { const parsedDoc: ParsedDoc = { source: { content: `--- @@ -105,11 +107,11 @@ destinations: # Content`, frontmatter: { - mixdown: 'v0', + mixdown: "v0", destinations: { cursor: { - outputPath: '.cursor/rules/test.mdc', - priority: 'high', + outputPath: ".cursor/rules/test.mdc", + priority: "high", }, }, }, @@ -123,28 +125,28 @@ destinations: }; const projectConfig = { - baseUrl: 'https://example.com', + baseUrl: "https://example.com", debug: true, }; - const result = compile(parsedDoc, 'cursor', projectConfig); + const result = compile(parsedDoc, "cursor", projectConfig); expect(result.context.config).toEqual({ - baseUrl: 'https://example.com', + baseUrl: "https://example.com", debug: true, - outputPath: '.cursor/rules/test.mdc', - priority: 'high', + outputPath: ".cursor/rules/test.mdc", + priority: "high", }); }); - it('should handle empty body after frontmatter', () => { + it("should handle empty body after frontmatter", () => { const parsedDoc: ParsedDoc = { source: { content: `--- mixdown: v0 ---`, frontmatter: { - mixdown: 'v0', + mixdown: "v0", }, }, ast: { @@ -155,12 +157,12 @@ mixdown: v0 }, }; - const result = compile(parsedDoc, 'cursor'); + const result = compile(parsedDoc, "cursor"); - expect(result.output.content).toBe(''); + expect(result.output.content).toBe(""); }); - it('should preserve markers in output for v0', () => { + it("should preserve markers in output for v0", () => { const parsedDoc: ParsedDoc = { source: { content: `--- @@ -175,7 +177,7 @@ Do not modify these markers in v0. The value is {{$myVariable}}.`, frontmatter: { - mixdown: 'v0', + mixdown: "v0", }, }, ast: { @@ -186,15 +188,15 @@ The value is {{$myVariable}}.`, }, }; - const result = compile(parsedDoc, 'cursor'); + const result = compile(parsedDoc, "cursor"); - expect(result.output.content).toContain('{{instructions}}'); - expect(result.output.content).toContain('{{/instructions}}'); - expect(result.output.content).toContain('{{> common-rules}}'); - expect(result.output.content).toContain('{{$myVariable}}'); + expect(result.output.content).toContain("{{instructions}}"); + expect(result.output.content).toContain("{{/instructions}}"); + expect(result.output.content).toContain("{{> common-rules}}"); + expect(result.output.content).toContain("{{$myVariable}}"); }); - it('should handle destination without config', () => { + it("should handle destination without config", () => { const parsedDoc: ParsedDoc = { source: { content: `--- @@ -207,9 +209,9 @@ destinations: # Content`, frontmatter: { - mixdown: 'v0', + mixdown: "v0", destinations: { - cursor: { path: '/test' }, + cursor: { path: "/test" }, windsurf: {}, }, }, @@ -222,7 +224,7 @@ destinations: }, }; - const result = compile(parsedDoc, 'windsurf'); + const result = compile(parsedDoc, "windsurf"); expect(result.context.config).toEqual({}); expect(result.output.metadata).toEqual({ @@ -232,4 +234,4 @@ destinations: }); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/src/compiler/index.ts b/packages/core/src/compiler/index.ts index 3508ebc..b8c389e 100644 --- a/packages/core/src/compiler/index.ts +++ b/packages/core/src/compiler/index.ts @@ -1,11 +1,11 @@ // :M: tldr: Compiler implementation for Rulesets notation // :M: v0.1.0: Pass-through implementation without marker processing -import type { ParsedDoc, CompiledDoc } from '../interfaces'; +import type { CompiledDoc, ParsedDoc } from "../interfaces"; /** * Compiles a parsed Rulesets document for a specific destination. * For v0.1.0, this is a pass-through implementation that doesn't process markers. - * + * * @param parsedDoc - The parsed document to compile * @param destinationId - The ID of the destination to compile for * @param projectConfig - Optional project configuration @@ -18,33 +18,35 @@ import type { ParsedDoc, CompiledDoc } from '../interfaces'; export function compile( parsedDoc: ParsedDoc, destinationId: string, - projectConfig: Record = {}, + projectConfig: Record = {} ): CompiledDoc { const { source, ast } = parsedDoc; - + // Handle empty files consistently if (!source.content.trim()) { - console.warn('Compiling empty source file'); } - + // Extract the body content (everything after frontmatter) let bodyContent = source.content; - + // If there's frontmatter, remove it from the body if (source.frontmatter) { - const lines = source.content.split('\n'); + const lines = source.content.split("\n"); let frontmatterEnd = -1; - - if (lines[0] === '---') { + + if (lines[0] === "---") { for (let i = 1; i < lines.length; i++) { - if (lines[i] === '---') { + if (lines[i] === "---") { frontmatterEnd = i; break; } } - + if (frontmatterEnd > 0) { - bodyContent = lines.slice(frontmatterEnd + 1).join('\n').trim(); + bodyContent = lines + .slice(frontmatterEnd + 1) + .join("\n") + .trim(); } } } @@ -57,13 +59,13 @@ export function compile( frontmatter: source.frontmatter, }, ast: { - stems: ast.stems, // Pass through from parser (empty for v0) - imports: ast.imports, // Pass through from parser (empty for v0) - variables: ast.variables, // Pass through from parser (empty for v0) - markers: ast.markers, // Pass through from parser (empty for v0) + stems: ast.stems, // Pass through from parser (empty for v0) + imports: ast.imports, // Pass through from parser (empty for v0) + variables: ast.variables, // Pass through from parser (empty for v0) + markers: ast.markers, // Pass through from parser (empty for v0) }, output: { - content: bodyContent, // Raw body content for v0 + content: bodyContent, // Raw body content for v0 metadata: { // Include relevant frontmatter metadata title: source.frontmatter?.title, @@ -84,4 +86,4 @@ export function compile( }; return compiledDoc; -} \ No newline at end of file +} diff --git a/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts b/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts index ad9de21..50a042f 100644 --- a/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts +++ b/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts @@ -1,18 +1,19 @@ // TLDR: Unit tests for the Cursor destination plugin (Rulesets v0) -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { promises as fs } from 'fs'; -import path from 'path'; -import { CursorPlugin } from '../cursor-plugin'; -import type { CompiledDoc, Logger } from '../../interfaces'; -vi.mock('fs', () => ({ +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { CompiledDoc, Logger } from "../../interfaces"; +import { CursorPlugin } from "../cursor-plugin"; + +vi.mock("fs", () => ({ promises: { mkdir: vi.fn(), writeFile: vi.fn(), }, })); -describe('CursorPlugin', () => { +describe("CursorPlugin", () => { let plugin: CursorPlugin; let mockLogger: Logger; @@ -31,34 +32,34 @@ describe('CursorPlugin', () => { vi.restoreAllMocks(); }); - describe('name', () => { + describe("name", () => { it('should return "cursor"', () => { - expect(plugin.name).toBe('cursor'); + expect(plugin.name).toBe("cursor"); }); }); - describe('configSchema', () => { - it('should return a valid JSON schema', () => { + describe("configSchema", () => { + it("should return a valid JSON schema", () => { const schema = plugin.configSchema(); - expect(schema.type).toBe('object'); + expect(schema.type).toBe("object"); expect(schema.properties).toBeDefined(); expect(schema.properties!.outputPath).toBeDefined(); expect(schema.properties!.priority).toBeDefined(); }); - it('should enforce priority enum values', () => { + it("should enforce priority enum values", () => { const schema = plugin.configSchema(); const priorityProp = schema.properties!.priority as { type: string; enum: string[] }; - expect(priorityProp.type).toBe('string'); - expect(priorityProp.enum).toEqual(['low', 'medium', 'high']); + expect(priorityProp.type).toBe("string"); + expect(priorityProp.enum).toEqual(["low", "medium", "high"]); }); }); - describe('write', () => { + describe("write", () => { const mockCompiledDoc: CompiledDoc = { source: { - content: '---\nrulesets: v0\n---\n\n# Test Content', - frontmatter: { rulesets: 'v0' }, + content: "---\nrulesets: v0\n---\n\n# Test Content", + frontmatter: { rulesets: "v0" }, }, ast: { stems: [], @@ -67,17 +68,17 @@ describe('CursorPlugin', () => { markers: [], }, output: { - content: '# Test Content\n\nThis is test content.', - metadata: { priority: 'high' }, + content: "# Test Content\n\nThis is test content.", + metadata: { priority: "high" }, }, context: { - destinationId: 'cursor', + destinationId: "cursor", config: {}, }, }; - it('should write content to the specified path', async () => { - const destPath = '.cursor/rules/test.mdc'; + it("should write content to the specified path", async () => { + const destPath = ".cursor/rules/test.mdc"; const config = {}; await plugin.write({ @@ -91,18 +92,18 @@ describe('CursorPlugin', () => { const dir = path.dirname(resolvedPath); expect(fs.mkdir).toHaveBeenCalledWith(dir, { recursive: true }); - expect(fs.writeFile).toHaveBeenCalledWith( - resolvedPath, - mockCompiledDoc.output.content, - { encoding: 'utf8' }, - ); + expect(fs.writeFile).toHaveBeenCalledWith(resolvedPath, mockCompiledDoc.output.content, { + encoding: "utf8", + }); expect(mockLogger.info).toHaveBeenCalledWith(`Writing Cursor rules to: ${destPath}`); - expect(mockLogger.info).toHaveBeenCalledWith(`Successfully wrote Cursor rules to: ${resolvedPath}`); + expect(mockLogger.info).toHaveBeenCalledWith( + `Successfully wrote Cursor rules to: ${resolvedPath}` + ); }); - it('should use outputPath from config if provided', async () => { - const destPath = '.cursor/rules/default.mdc'; - const config = { outputPath: '.cursor/rules/custom.mdc' }; + it("should use outputPath from config if provided", async () => { + const destPath = ".cursor/rules/default.mdc"; + const config = { outputPath: ".cursor/rules/custom.mdc" }; await plugin.write({ compiled: mockCompiledDoc, @@ -112,16 +113,14 @@ describe('CursorPlugin', () => { }); const resolvedPath = path.resolve(config.outputPath); - expect(fs.writeFile).toHaveBeenCalledWith( - resolvedPath, - mockCompiledDoc.output.content, - { encoding: 'utf8' }, - ); + expect(fs.writeFile).toHaveBeenCalledWith(resolvedPath, mockCompiledDoc.output.content, { + encoding: "utf8", + }); }); - it('should log debug information', async () => { - const destPath = '.cursor/rules/test.mdc'; - const config = { priority: 'high' }; + it("should log debug information", async () => { + const destPath = ".cursor/rules/test.mdc"; + const config = { priority: "high" }; await plugin.write({ compiled: mockCompiledDoc, @@ -130,16 +129,14 @@ describe('CursorPlugin', () => { logger: mockLogger, }); - expect(mockLogger.debug).toHaveBeenCalledWith('Destination: cursor'); - expect(mockLogger.debug).toHaveBeenCalledWith( - expect.stringContaining('Config:'), - ); - expect(mockLogger.debug).toHaveBeenCalledWith('Priority: high'); + expect(mockLogger.debug).toHaveBeenCalledWith("Destination: cursor"); + expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining("Config:")); + expect(mockLogger.debug).toHaveBeenCalledWith("Priority: high"); }); - it('should handle mkdir errors', async () => { - const destPath = '.cursor/rules/test.mdc'; - const error = new Error('Permission denied'); + it("should handle mkdir errors", async () => { + const destPath = ".cursor/rules/test.mdc"; + const error = new Error("Permission denied"); vi.mocked(fs.mkdir).mockRejectedValueOnce(error); await expect( @@ -148,18 +145,18 @@ describe('CursorPlugin', () => { destPath, config: {}, logger: mockLogger, - }), - ).rejects.toThrow('Permission denied'); + }) + ).rejects.toThrow("Permission denied"); expect(mockLogger.error).toHaveBeenCalledWith( - expect.stringContaining('Failed to create directory'), - error, + expect.stringContaining("Failed to create directory"), + error ); }); - it('should handle writeFile errors', async () => { - const destPath = '.cursor/rules/test.mdc'; - const error = new Error('Disk full'); + it("should handle writeFile errors", async () => { + const destPath = ".cursor/rules/test.mdc"; + const error = new Error("Disk full"); vi.mocked(fs.writeFile).mockRejectedValueOnce(error); await expect( @@ -168,13 +165,13 @@ describe('CursorPlugin', () => { destPath, config: {}, logger: mockLogger, - }), - ).rejects.toThrow('Disk full'); + }) + ).rejects.toThrow("Disk full"); expect(mockLogger.error).toHaveBeenCalledWith( - expect.stringContaining('Failed to write file'), - error, + expect.stringContaining("Failed to write file"), + error ); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts b/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts index 9fb77f8..bca3a7f 100644 --- a/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts +++ b/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts @@ -1,18 +1,19 @@ // TLDR: Unit tests for the Windsurf destination plugin (mixd-v0) -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { promises as fs } from 'fs'; -import path from 'path'; -import { WindsurfPlugin } from '../windsurf-plugin'; -import type { CompiledDoc, Logger } from '../../interfaces'; -vi.mock('fs', () => ({ +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { CompiledDoc, Logger } from "../../interfaces"; +import { WindsurfPlugin } from "../windsurf-plugin"; + +vi.mock("fs", () => ({ promises: { mkdir: vi.fn(), writeFile: vi.fn(), }, })); -describe('WindsurfPlugin', () => { +describe("WindsurfPlugin", () => { let plugin: WindsurfPlugin; let mockLogger: Logger; @@ -31,32 +32,32 @@ describe('WindsurfPlugin', () => { vi.restoreAllMocks(); }); - describe('name', () => { + describe("name", () => { it('should return "windsurf"', () => { - expect(plugin.name).toBe('windsurf'); + expect(plugin.name).toBe("windsurf"); }); }); - describe('configSchema', () => { - it('should return a valid JSON schema', () => { + describe("configSchema", () => { + it("should return a valid JSON schema", () => { const schema = plugin.configSchema(); - expect(schema.type).toBe('object'); + expect(schema.type).toBe("object"); expect(schema.properties).toBeDefined(); expect(schema.properties!.outputPath).toBeDefined(); expect(schema.properties!.format).toBeDefined(); - + const formatProp = schema.properties!.format as { enum: string[]; default: string }; - expect(formatProp.enum).toContain('markdown'); - expect(formatProp.enum).toContain('xml'); - expect(formatProp.default).toBe('markdown'); + expect(formatProp.enum).toContain("markdown"); + expect(formatProp.enum).toContain("xml"); + expect(formatProp.default).toBe("markdown"); }); }); - describe('write', () => { + describe("write", () => { const mockCompiledDoc: CompiledDoc = { source: { - content: '---\nmixdown: v0\n---\n\n# Test Content', - frontmatter: { mixdown: 'v0' }, + content: "---\nmixdown: v0\n---\n\n# Test Content", + frontmatter: { mixdown: "v0" }, }, ast: { stems: [], @@ -65,17 +66,17 @@ describe('WindsurfPlugin', () => { markers: [], }, output: { - content: '# Test Content\n\nThis is test content.', + content: "# Test Content\n\nThis is test content.", metadata: {}, }, context: { - destinationId: 'windsurf', + destinationId: "windsurf", config: {}, }, }; - it('should write content to the specified path', async () => { - const destPath = '.windsurf/rules/test.md'; + it("should write content to the specified path", async () => { + const destPath = ".windsurf/rules/test.md"; const config = {}; await plugin.write({ @@ -92,15 +93,17 @@ describe('WindsurfPlugin', () => { expect(fs.writeFile).toHaveBeenCalledWith( resolvedPath, mockCompiledDoc.output.content, - 'utf-8', + "utf-8" ); expect(mockLogger.info).toHaveBeenCalledWith(`Writing Windsurf rules to: ${resolvedPath}`); - expect(mockLogger.info).toHaveBeenCalledWith(`Successfully wrote Windsurf rules to: ${resolvedPath}`); + expect(mockLogger.info).toHaveBeenCalledWith( + `Successfully wrote Windsurf rules to: ${resolvedPath}` + ); }); - it('should use outputPath from config if provided', async () => { - const destPath = '.windsurf/rules/default.md'; - const config = { outputPath: '.windsurf/rules/custom.md' }; + it("should use outputPath from config if provided", async () => { + const destPath = ".windsurf/rules/default.md"; + const config = { outputPath: ".windsurf/rules/custom.md" }; await plugin.write({ compiled: mockCompiledDoc, @@ -113,13 +116,13 @@ describe('WindsurfPlugin', () => { expect(fs.writeFile).toHaveBeenCalledWith( resolvedPath, mockCompiledDoc.output.content, - 'utf-8', + "utf-8" ); }); - it('should log format information', async () => { - const destPath = '.windsurf/rules/test.md'; - const config = { format: 'xml' }; + it("should log format information", async () => { + const destPath = ".windsurf/rules/test.md"; + const config = { format: "xml" }; await plugin.write({ compiled: mockCompiledDoc, @@ -128,13 +131,13 @@ describe('WindsurfPlugin', () => { logger: mockLogger, }); - expect(mockLogger.debug).toHaveBeenCalledWith('Destination: windsurf'); + expect(mockLogger.debug).toHaveBeenCalledWith("Destination: windsurf"); expect(mockLogger.debug).toHaveBeenCalledWith(`Config: ${JSON.stringify(config)}`); - expect(mockLogger.debug).toHaveBeenCalledWith('Format: xml'); + expect(mockLogger.debug).toHaveBeenCalledWith("Format: xml"); }); - it('should default to markdown format', async () => { - const destPath = '.windsurf/rules/test.md'; + it("should default to markdown format", async () => { + const destPath = ".windsurf/rules/test.md"; const config = {}; await plugin.write({ @@ -144,12 +147,12 @@ describe('WindsurfPlugin', () => { logger: mockLogger, }); - expect(mockLogger.debug).toHaveBeenCalledWith('Format: markdown'); + expect(mockLogger.debug).toHaveBeenCalledWith("Format: markdown"); }); - it('should handle mkdir errors', async () => { - const destPath = '.windsurf/rules/test.md'; - const error = new Error('Permission denied'); + it("should handle mkdir errors", async () => { + const destPath = ".windsurf/rules/test.md"; + const error = new Error("Permission denied"); vi.mocked(fs.mkdir).mockRejectedValueOnce(error); await expect( @@ -158,18 +161,18 @@ describe('WindsurfPlugin', () => { destPath, config: {}, logger: mockLogger, - }), - ).rejects.toThrow('Permission denied'); + }) + ).rejects.toThrow("Permission denied"); expect(mockLogger.error).toHaveBeenCalledWith( - expect.stringContaining('Failed to create directory'), - error, + expect.stringContaining("Failed to create directory"), + error ); }); - it('should handle writeFile errors', async () => { - const destPath = '.windsurf/rules/test.md'; - const error = new Error('Disk full'); + it("should handle writeFile errors", async () => { + const destPath = ".windsurf/rules/test.md"; + const error = new Error("Disk full"); vi.mocked(fs.writeFile).mockRejectedValueOnce(error); await expect( @@ -178,13 +181,13 @@ describe('WindsurfPlugin', () => { destPath, config: {}, logger: mockLogger, - }), - ).rejects.toThrow('Disk full'); + }) + ).rejects.toThrow("Disk full"); expect(mockLogger.error).toHaveBeenCalledWith( - expect.stringContaining('Failed to write file'), - error, + expect.stringContaining("Failed to write file"), + error ); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/src/destinations/cursor-plugin.ts b/packages/core/src/destinations/cursor-plugin.ts index be66e53..7e32fcd 100644 --- a/packages/core/src/destinations/cursor-plugin.ts +++ b/packages/core/src/destinations/cursor-plugin.ts @@ -1,30 +1,30 @@ // :M: tldr: Cursor destination plugin implementation // :M: v0.1.0: Stub implementation that writes raw content to .cursor/rules/ -import { promises as fs } from 'fs'; -import path from 'path'; -import type { DestinationPlugin, CompiledDoc, Logger, JSONSchema7 } from '../interfaces'; +import { promises as fs } from "node:fs"; +import path from "node:path"; +import type { CompiledDoc, DestinationPlugin, JSONSchema7, Logger } from "../interfaces"; export class CursorPlugin implements DestinationPlugin { // :M: tldr: Returns the canonical name for Cursor destination // :M: v0.1.0: Static identifier for plugin registration get name(): string { - return 'cursor'; + return "cursor"; } // :M: tldr: Provides configuration schema for Cursor plugin // :M: v0.1.0: Basic schema with outputPath and priority options configSchema(): JSONSchema7 { return { - type: 'object', + type: "object", properties: { outputPath: { - type: 'string', - description: 'Path where the compiled rules file should be written', + type: "string", + description: "Path where the compiled rules file should be written", }, priority: { - type: 'string', - enum: ['low', 'medium', 'high'], - description: 'Priority level for the rules', + type: "string", + enum: ["low", "medium", "high"], + description: "Priority level for the rules", }, }, additionalProperties: true, @@ -46,9 +46,7 @@ export class CursorPlugin implements DestinationPlugin { // Determine the output path const outputPath = config.outputPath || destPath; - const resolvedPath = path.isAbsolute(outputPath) - ? outputPath - : path.resolve(outputPath); + const resolvedPath = path.isAbsolute(outputPath) ? outputPath : path.resolve(outputPath); // Ensure directory exists const dir = path.dirname(resolvedPath); @@ -61,9 +59,9 @@ export class CursorPlugin implements DestinationPlugin { // For v0, write the raw content try { - await fs.writeFile(resolvedPath, compiled.output.content, { encoding: 'utf8' }); + await fs.writeFile(resolvedPath, compiled.output.content, { encoding: "utf8" }); logger.info(`Successfully wrote Cursor rules to: ${resolvedPath}`); - + // Log additional context for debugging logger.debug(`Destination: ${compiled.context.destinationId}`); logger.debug(`Config: ${JSON.stringify(config)}`); @@ -75,4 +73,4 @@ export class CursorPlugin implements DestinationPlugin { throw error; } } -} \ No newline at end of file +} diff --git a/packages/core/src/destinations/index.ts b/packages/core/src/destinations/index.ts index 1cd8db8..f291c3c 100644 --- a/packages/core/src/destinations/index.ts +++ b/packages/core/src/destinations/index.ts @@ -1,8 +1,9 @@ // :M: tldr: Export destination plugin instances for use in Rulesets // :M: v0.1.0: Basic plugin registry with Cursor and Windsurf support -import { CursorPlugin } from './cursor-plugin'; -import { WindsurfPlugin } from './windsurf-plugin'; -import type { DestinationPlugin } from '../interfaces'; + +import type { DestinationPlugin } from "../interfaces"; +import { CursorPlugin } from "./cursor-plugin"; +import { WindsurfPlugin } from "./windsurf-plugin"; // Create singleton instances export const cursorPlugin = new CursorPlugin(); @@ -10,9 +11,9 @@ export const windsurfPlugin = new WindsurfPlugin(); // Export as a map for easy lookup export const destinations: ReadonlyMap = new Map([ - ['cursor', cursorPlugin], - ['windsurf', windsurfPlugin], + ["cursor", cursorPlugin], + ["windsurf", windsurfPlugin], ]); // Export classes for testing and extension -export { CursorPlugin, WindsurfPlugin }; \ No newline at end of file +export { CursorPlugin, WindsurfPlugin }; diff --git a/packages/core/src/destinations/windsurf-plugin.ts b/packages/core/src/destinations/windsurf-plugin.ts index 96f7d09..4831baa 100644 --- a/packages/core/src/destinations/windsurf-plugin.ts +++ b/packages/core/src/destinations/windsurf-plugin.ts @@ -1,31 +1,31 @@ // :M: tldr: Windsurf destination plugin implementation // :M: v0.1.0: Stub implementation that writes raw content to .windsurf/rules/ -import { promises as fs } from 'fs'; -import path from 'path'; -import type { DestinationPlugin, CompiledDoc, Logger, JSONSchema7 } from '../interfaces'; +import { promises as fs } from "node:fs"; +import path from "node:path"; +import type { CompiledDoc, DestinationPlugin, JSONSchema7, Logger } from "../interfaces"; export class WindsurfPlugin implements DestinationPlugin { // :M: tldr: Returns the canonical name for Windsurf destination // :M: v0.1.0: Static identifier for plugin registration get name(): string { - return 'windsurf'; + return "windsurf"; } // :M: tldr: Provides configuration schema for Windsurf plugin // :M: v0.1.0: Basic schema with outputPath and format options configSchema(): JSONSchema7 { return { - type: 'object', + type: "object", properties: { outputPath: { - type: 'string', - description: 'Path where the compiled rules file should be written', + type: "string", + description: "Path where the compiled rules file should be written", }, format: { - type: 'string', - enum: ['markdown', 'xml'], - default: 'markdown', - description: 'Output format for Windsurf rules', + type: "string", + enum: ["markdown", "xml"], + default: "markdown", + description: "Output format for Windsurf rules", }, }, additionalProperties: true, @@ -46,7 +46,7 @@ export class WindsurfPlugin implements DestinationPlugin { // Determine the output path const outputPath = config.outputPath || destPath; const resolvedPath = path.resolve(outputPath); - + logger.info(`Writing Windsurf rules to: ${resolvedPath}`); // Ensure directory exists @@ -60,16 +60,16 @@ export class WindsurfPlugin implements DestinationPlugin { // For v0, write the raw content try { - await fs.writeFile(resolvedPath, compiled.output.content, 'utf-8'); + await fs.writeFile(resolvedPath, compiled.output.content, "utf-8"); logger.info(`Successfully wrote Windsurf rules to: ${resolvedPath}`); - + // Log additional context for debugging logger.debug(`Destination: ${compiled.context.destinationId}`); logger.debug(`Config: ${JSON.stringify(config)}`); - logger.debug(`Format: ${config.format || 'markdown'}`); + logger.debug(`Format: ${config.format || "markdown"}`); } catch (error) { logger.error(`Failed to write file: ${resolvedPath}`, error); throw error; } } -} \ No newline at end of file +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d2b9102..8f8085a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,19 +1,19 @@ // :M: tldr: Main entry point and CLI orchestration for Rulesets // :M: v0.1.0: Basic orchestration for single file processing with minimal features -import { promises as fs } from 'fs'; -import { parse } from './parser'; -import { lint } from './linter'; -import { compile } from './compiler'; -import { destinations } from './destinations'; -import { ConsoleLogger } from './interfaces'; -import type { Logger } from './interfaces'; - +import { promises as fs } from "node:fs"; +import { compile } from "./compiler"; +import { destinations } from "./destinations"; +import type { Logger } from "./interfaces"; +import { ConsoleLogger } from "./interfaces"; +import { lint } from "./linter"; +import { parse } from "./parser"; + +export { compile } from "./compiler"; +export { CursorPlugin, destinations, WindsurfPlugin } from "./destinations"; // Export all public APIs -export * from './interfaces'; -export { parse } from './parser'; -export { lint, type LinterConfig, type LintResult } from './linter'; -export { compile } from './compiler'; -export { destinations, CursorPlugin, WindsurfPlugin } from './destinations'; +export * from "./interfaces"; +export { type LinterConfig, type LintResult, lint } from "./linter"; +export { parse } from "./parser"; /** * Orchestrates the Rulesets v0.1.0 build process for a single file. @@ -47,14 +47,14 @@ export { destinations, CursorPlugin, WindsurfPlugin } from './destinations'; export async function runRulesetsV0( sourceFilePath: string, logger: Logger = new ConsoleLogger(), - projectConfig: Record = {}, + projectConfig: Record = {} ): Promise { logger.info(`Starting Rulesets v0.1.0 processing for: ${sourceFilePath}`); // Step 1: Read the source file let content: string; try { - content = await fs.readFile(sourceFilePath, 'utf-8'); + content = await fs.readFile(sourceFilePath, "utf-8"); logger.debug(`Read ${content.length} characters from ${sourceFilePath}`); } catch (error) { logger.error(`Failed to read source file: ${sourceFilePath}`, error); @@ -62,22 +62,22 @@ export async function runRulesetsV0( } // Step 2: Parse the content - logger.info('Parsing source file...'); + logger.info("Parsing source file..."); let parsedDoc; try { parsedDoc = await parse(content); parsedDoc.source.path = sourceFilePath; - + if (parsedDoc.errors && parsedDoc.errors.length > 0) { logger.warn(`Parser found ${parsedDoc.errors.length} error(s)`); } } catch (error) { - logger.error('Failed to parse source file', error); + logger.error("Failed to parse source file", error); throw error; } // Step 3: Lint the parsed document - logger.info('Linting document...'); + logger.info("Linting document..."); let lintResults; try { lintResults = await lint(parsedDoc, { @@ -88,18 +88,18 @@ export async function runRulesetsV0( // Log lint results let hasErrors = false; for (const result of lintResults) { - const location = result.line ? ` (line ${result.line})` : ''; + const location = result.line ? ` (line ${result.line})` : ""; const message = `${result.message}${location}`; - + switch (result.severity) { - case 'error': + case "error": logger.error(message); hasErrors = true; break; - case 'warning': + case "warning": logger.warn(message); break; - case 'info': + case "info": logger.info(message); break; } @@ -107,20 +107,20 @@ export async function runRulesetsV0( // Stop if there are lint errors if (hasErrors) { - throw new Error('Linting failed with errors. Please fix the issues and try again.'); + throw new Error("Linting failed with errors. Please fix the issues and try again."); } } catch (error) { - logger.error('Failed during linting', error); + logger.error("Failed during linting", error); throw error; } // Step 4: Determine which destinations to compile for const frontmatter = parsedDoc.source.frontmatter || {}; - const destinationIds = frontmatter.destinations + const destinationIds = frontmatter.destinations ? Object.keys(frontmatter.destinations) : Array.from(destinations.keys()); - logger.info(`Compiling for destinations: ${destinationIds.join(', ')}`); + logger.info(`Compiling for destinations: ${destinationIds.join(", ")}`); // Step 5: Compile and write for each destination for (const destinationId of destinationIds) { @@ -160,7 +160,7 @@ export async function runRulesetsV0( } } - logger.info('Rulesets v0.1.0 processing completed successfully!'); + logger.info("Rulesets v0.1.0 processing completed successfully!"); } // CLI entry point for testing @@ -169,15 +169,15 @@ export async function runRulesetsV0( // :M: todo(v0.2.0): Replace with proper CLI using commander or yargs if (require.main === module) { const logger = new ConsoleLogger(); - const sourceFile = process.argv[2] || './my-rules.mix.md'; - + const sourceFile = process.argv[2] || "./my-rules.mix.md"; + runRulesetsV0(sourceFile, logger) .then(() => { - logger.info('Done!'); + logger.info("Done!"); process.exit(0); }) .catch((error) => { - logger.error('Failed:', error); + logger.error("Failed:", error); process.exit(1); }); -} \ No newline at end of file +} diff --git a/packages/core/src/interfaces/compiled-doc.ts b/packages/core/src/interfaces/compiled-doc.ts index b5827f5..061ad9c 100644 --- a/packages/core/src/interfaces/compiled-doc.ts +++ b/packages/core/src/interfaces/compiled-doc.ts @@ -5,72 +5,72 @@ * Represents the structure of a parsed Rulesets stem. * For v0.1.0, this will be minimal as stems are not processed from the body. */ -export interface Stem { +export type Stem = { name: string; // properties: Record; // To be detailed in v0.1+ // content: string; // To be detailed in v0.1+ // rawMarker: string; // To be detailed in v0.1+ -} +}; /** * Represents the structure of a parsed Rulesets import. * For v0.1.0, this will be minimal. */ -export interface Import { +export type Import = { path: string; // properties: Record; // To be detailed in v0.1+ // rawMarker: string; // To be detailed in v0.1+ -} +}; /** * Represents the structure of a parsed Rulesets variable. * For v0.1.0, this will be minimal. */ -export interface Variable { +export type Variable = { name: string; // rawMarker: string; // To be detailed in v0.1+ -} +}; /** * Represents the structure of a generic Rulesets marker. * For v0.1.0, this will be minimal. */ -export interface Marker { - type: 'stem' | 'import' | 'variable' | 'unknown'; +export type Marker = { + type: "stem" | "import" | "variable" | "unknown"; // rawMarker: string; // To be detailed in v0.1+ // position: { line: number, column: number }; // To be detailed in v0.1+ -} - +}; /** * Represents a document that has been parsed by the Rulesets parser. * This is an intermediate representation before full compilation. */ -export interface ParsedDoc { +export type ParsedDoc = { source: { path?: string; // Original source file path, if applicable content: string; // Raw source content frontmatter?: Record; // Parsed frontmatter data }; - ast: { // Abstract Syntax Tree - minimal for v0 + ast: { + // Abstract Syntax Tree - minimal for v0 stems: Stem[]; imports: Import[]; variables: Variable[]; markers: Marker[]; // All markers found - empty for v0 body processing }; errors?: Array<{ message: string; line?: number; column?: number }>; // Parsing errors -} +}; /** * Represents a document that has been compiled for a specific destination. * This is the primary data structure passed to destination plugins. */ -export interface CompiledDoc { +export type CompiledDoc = { /** Original source content and metadata */ source: { - path?: string; // Original source file path, if applicable - content: string; // Raw source content - frontmatter?: Record; // Parsed frontmatter data + path?: string; // Original source file path, if applicable + content: string; // Raw source content + frontmatter?: Record; // Parsed frontmatter data }; /** @@ -80,15 +80,15 @@ export interface CompiledDoc { * The primary focus for v0.1.0 body content is that it's not processed for markers. */ ast: { - stems: Stem[]; // Array of parsed stems (empty for v0 body) - imports: Import[]; // Array of parsed imports (empty for v0 body) + stems: Stem[]; // Array of parsed stems (empty for v0 body) + imports: Import[]; // Array of parsed imports (empty for v0 body) variables: Variable[]; // Array of parsed variables (empty for v0 body) - markers: Marker[]; // All markers found in the document (empty for v0 body) + markers: Marker[]; // All markers found in the document (empty for v0 body) }; /** Destination-specific output */ output: { - content: string; // Transformed content for the destination (raw body for v0) + content: string; // Transformed content for the destination (raw body for v0) metadata?: Record; // Any metadata needed by the destination (e.g., derived from frontmatter) }; @@ -97,4 +97,4 @@ export interface CompiledDoc { destinationId: string; // Current destination being compiled for config: Record; // Resolved configuration for this compilation (e.g., project config, destination-specific config) }; -} \ No newline at end of file +}; diff --git a/packages/core/src/interfaces/destination-plugin.ts b/packages/core/src/interfaces/destination-plugin.ts index bc6d430..1eed54d 100644 --- a/packages/core/src/interfaces/destination-plugin.ts +++ b/packages/core/src/interfaces/destination-plugin.ts @@ -1,12 +1,12 @@ // :M: tldr: Defines the DestinationPlugin interface for Rulesets // :M: v0.1.0: Basic plugin contract for writing compiled rules to destinations -import type { JSONSchema7 } from 'json-schema'; -import type { CompiledDoc } from './compiled-doc'; -import type { Logger } from './logger'; +import type { JSONSchema7 } from "json-schema"; +import type { CompiledDoc } from "./compiled-doc"; +import type { Logger } from "./logger"; export type { JSONSchema7 }; // Re-export for convenience -export interface DestinationPlugin { +export type DestinationPlugin = { /** * Canonical ID for the destination plugin. * Should be unique, kebab-case. e.g., "cursor", "windsurf". @@ -44,4 +44,4 @@ export interface DestinationPlugin { config: Record; // Validated via schema from configSchema() logger: Logger; }): Promise; -} \ No newline at end of file +}; diff --git a/packages/core/src/interfaces/index.ts b/packages/core/src/interfaces/index.ts index 61d815c..6922eb4 100644 --- a/packages/core/src/interfaces/index.ts +++ b/packages/core/src/interfaces/index.ts @@ -1,4 +1,5 @@ // TLDR: Export all interfaces from the interfaces directory (mixd-v0) -export * from './logger'; -export * from './compiled-doc'; -export * from './destination-plugin'; \ No newline at end of file + +export * from "./compiled-doc"; +export * from "./destination-plugin"; +export * from "./logger"; diff --git a/packages/core/src/interfaces/logger.ts b/packages/core/src/interfaces/logger.ts index db8af8e..95f7c60 100644 --- a/packages/core/src/interfaces/logger.ts +++ b/packages/core/src/interfaces/logger.ts @@ -1,6 +1,6 @@ // :M: tldr: Defines the Logger interface for Rulesets // :M: v0.1.0: Simple logging contract with four standard levels -export interface Logger { +export type Logger = { // :M: tldr: Logs a debug message // :M: v0.1.0: For detailed debugging information debug(message: string, ...args: unknown[]): void; @@ -13,34 +13,27 @@ export interface Logger { // :M: tldr: Logs an error message // :M: v0.1.0: For errors that require user attention error(message: string | Error, ...args: unknown[]): void; -} +}; // Basic console logger implementation for v0 export class ConsoleLogger implements Logger { // :M: tldr: Logs a debug message to the console // :M: v0.1.0: Console implementation of debug logging - public debug(message: string, ...args: unknown[]): void { - if (process.env.RULESETS_LOG_LEVEL === 'debug') { - console.debug(`[DEBUG] ${message}`, ...args); + public debug(_message: string, ..._args: unknown[]): void { + if (process.env.RULESETS_LOG_LEVEL === "debug") { } } // :M: tldr: Logs an informational message to the console // :M: v0.1.0: Console implementation of info logging - public info(message: string, ...args: unknown[]): void { - console.info(`[INFO] ${message}`, ...args); - } + public info(_message: string, ..._args: unknown[]): void {} // :M: tldr: Logs a warning message to the console // :M: v0.1.0: Console implementation of warning logging - public warn(message: string, ...args: unknown[]): void { - console.warn(`[WARN] ${message}`, ...args); - } + public warn(_message: string, ..._args: unknown[]): void {} // :M: tldr: Logs an error message to the console // :M: v0.1.0: Console implementation with stack trace support - public error(message: string | Error, ...args: unknown[]): void { + public error(message: string | Error, ..._args: unknown[]): void { if (message instanceof Error) { - console.error(`[ERROR] ${message.message}`, message.stack, ...args); } else { - console.error(`[ERROR] ${message}`, ...args); } } -} \ No newline at end of file +} diff --git a/packages/core/src/linter/__tests__/linter.spec.ts b/packages/core/src/linter/__tests__/linter.spec.ts index 669ed31..d36df7b 100644 --- a/packages/core/src/linter/__tests__/linter.spec.ts +++ b/packages/core/src/linter/__tests__/linter.spec.ts @@ -1,18 +1,18 @@ // TLDR: Unit tests for the Rulesets linter module (mixd-v0) -import { describe, it, expect } from 'vitest'; -import { lint } from '../index'; -import type { ParsedDoc } from '../../interfaces'; +import { describe, expect, it } from "vitest"; +import type { ParsedDoc } from "../../interfaces"; +import { lint } from "../index"; -describe('linter', () => { - describe('lint', () => { - it('should pass a valid document with complete frontmatter', async () => { +describe("linter", () => { + describe("lint", () => { + it("should pass a valid document with complete frontmatter", async () => { const parsedDoc: ParsedDoc = { source: { - content: '---\nmixdown: v0\ntitle: Test\ndescription: Test description\n---\n\n# Content', + content: "---\nmixdown: v0\ntitle: Test\ndescription: Test description\n---\n\n# Content", frontmatter: { - mixdown: 'v0', - title: 'Test', - description: 'Test description', + mixdown: "v0", + title: "Test", + description: "Test description", }, }, ast: { @@ -27,10 +27,10 @@ describe('linter', () => { expect(results).toHaveLength(0); }); - it('should warn when no frontmatter is present', async () => { + it("should warn when no frontmatter is present", async () => { const parsedDoc: ParsedDoc = { source: { - content: '# Content without frontmatter', + content: "# Content without frontmatter", }, ast: { stems: [], @@ -42,16 +42,16 @@ describe('linter', () => { const results = await lint(parsedDoc); expect(results).toHaveLength(1); - expect(results[0].severity).toBe('warning'); - expect(results[0].message).toContain('No frontmatter found'); + expect(results[0].severity).toBe("warning"); + expect(results[0].message).toContain("No frontmatter found"); }); - it('should error when mixdown version is missing', async () => { + it("should error when mixdown version is missing", async () => { const parsedDoc: ParsedDoc = { source: { - content: '---\ntitle: Test\n---\n\n# Content', + content: "---\ntitle: Test\n---\n\n# Content", frontmatter: { - title: 'Test', + title: "Test", }, }, ast: { @@ -63,15 +63,17 @@ describe('linter', () => { }; const results = await lint(parsedDoc); - const mixdownError = results.find(r => r.message.includes('Missing required "mixdown" field')); + const mixdownError = results.find((r) => + r.message.includes('Missing required "mixdown" field') + ); expect(mixdownError).toBeDefined(); - expect(mixdownError!.severity).toBe('error'); + expect(mixdownError!.severity).toBe("error"); }); - it('should error when mixdown field is not a string', async () => { + it("should error when mixdown field is not a string", async () => { const parsedDoc: ParsedDoc = { source: { - content: '---\nmixdown: 123\n---\n\n# Content', + content: "---\nmixdown: 123\n---\n\n# Content", frontmatter: { mixdown: 123, }, @@ -85,18 +87,18 @@ describe('linter', () => { }; const results = await lint(parsedDoc); - const typeError = results.find(r => r.message.includes('Invalid "mixdown" field type')); + const typeError = results.find((r) => r.message.includes('Invalid "mixdown" field type')); expect(typeError).toBeDefined(); - expect(typeError!.severity).toBe('error'); + expect(typeError!.severity).toBe("error"); }); - it('should validate destinations structure', async () => { + it("should validate destinations structure", async () => { const parsedDoc: ParsedDoc = { source: { content: '---\nmixdown: v0\ndestinations: ["cursor", "windsurf"]\n---\n\n# Content', frontmatter: { - mixdown: 'v0', - destinations: ['cursor', 'windsurf'], + mixdown: "v0", + destinations: ["cursor", "windsurf"], }, }, ast: { @@ -108,19 +110,20 @@ describe('linter', () => { }; const results = await lint(parsedDoc); - const destError = results.find(r => r.message.includes('Invalid "destinations" field')); + const destError = results.find((r) => r.message.includes('Invalid "destinations" field')); expect(destError).toBeDefined(); - expect(destError!.severity).toBe('error'); + expect(destError!.severity).toBe("error"); }); - it('should warn about unknown destinations when configured', async () => { + it("should warn about unknown destinations when configured", async () => { const parsedDoc: ParsedDoc = { source: { - content: '---\nmixdown: v0\ndestinations:\n unknown-dest:\n path: "/test"\n---\n\n# Content', + content: + '---\nmixdown: v0\ndestinations:\n unknown-dest:\n path: "/test"\n---\n\n# Content', frontmatter: { - mixdown: 'v0', + mixdown: "v0", destinations: { - 'unknown-dest': { path: '/test' }, + "unknown-dest": { path: "/test" }, }, }, }, @@ -133,20 +136,20 @@ describe('linter', () => { }; const results = await lint(parsedDoc, { - allowedDestinations: ['cursor', 'windsurf'], + allowedDestinations: ["cursor", "windsurf"], }); - const destWarning = results.find(r => r.message.includes('Unknown destination')); + const destWarning = results.find((r) => r.message.includes("Unknown destination")); expect(destWarning).toBeDefined(); - expect(destWarning!.severity).toBe('warning'); + expect(destWarning!.severity).toBe("warning"); }); - it('should provide info suggestions for missing title and description', async () => { + it("should provide info suggestions for missing title and description", async () => { const parsedDoc: ParsedDoc = { source: { - content: '---\nmixdown: v0\n---\n\n# Content', + content: "---\nmixdown: v0\n---\n\n# Content", frontmatter: { - mixdown: 'v0', + mixdown: "v0", }, }, ast: { @@ -158,19 +161,19 @@ describe('linter', () => { }; const results = await lint(parsedDoc); - const titleInfo = results.find(r => r.message.includes('Consider adding a "title"')); - const descInfo = results.find(r => r.message.includes('Consider adding a "description"')); - + const titleInfo = results.find((r) => r.message.includes('Consider adding a "title"')); + const descInfo = results.find((r) => r.message.includes('Consider adding a "description"')); + expect(titleInfo).toBeDefined(); - expect(titleInfo!.severity).toBe('info'); + expect(titleInfo!.severity).toBe("info"); expect(descInfo).toBeDefined(); - expect(descInfo!.severity).toBe('info'); + expect(descInfo!.severity).toBe("info"); }); - it('should include parsing errors in lint results', async () => { + it("should include parsing errors in lint results", async () => { const parsedDoc: ParsedDoc = { source: { - content: '---\ninvalid yaml\n---\n\n# Content', + content: "---\ninvalid yaml\n---\n\n# Content", frontmatter: {}, }, ast: { @@ -181,7 +184,7 @@ describe('linter', () => { }, errors: [ { - message: 'Failed to parse frontmatter YAML', + message: "Failed to parse frontmatter YAML", line: 2, column: 1, }, @@ -189,10 +192,12 @@ describe('linter', () => { }; const results = await lint(parsedDoc); - const parseError = results.find(r => r.message.includes('Failed to parse frontmatter YAML')); + const parseError = results.find((r) => + r.message.includes("Failed to parse frontmatter YAML") + ); expect(parseError).toBeDefined(); - expect(parseError!.severity).toBe('error'); + expect(parseError!.severity).toBe("error"); expect(parseError!.line).toBe(2); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/src/linter/index.ts b/packages/core/src/linter/index.ts index bcf0987..555b93a 100644 --- a/packages/core/src/linter/index.ts +++ b/packages/core/src/linter/index.ts @@ -1,27 +1,27 @@ // :M: tldr: Linter implementation for Rulesets notation // :M: v0.1.0: Basic frontmatter schema validation only -import type { ParsedDoc } from '../interfaces'; +import type { ParsedDoc } from "../interfaces"; -export interface LinterConfig { +export type LinterConfig = { requireRulesetsVersion?: boolean; allowedDestinations?: string[]; -} +}; -export interface LintResult { +export type LintResult = { message: string; line?: number; column?: number; - severity: 'error' | 'warning' | 'info'; -} + severity: "error" | "warning" | "info"; +}; // Human-readable field names for error messages const FIELD_NAMES: Record = { - '/rulesets': 'Rulesets version declaration', - '/rulesets/version': 'Rulesets version number', - '/destinations': 'Destination configurations', - '/title': 'Document title', - '/description': 'Document description', - '/version': 'Document version', + "/rulesets": "Rulesets version declaration", + "/rulesets/version": "Rulesets version number", + "/destinations": "Destination configurations", + "/title": "Document title", + "/description": "Document description", + "/version": "Document version", }; function getFieldName(path: string): string { @@ -31,7 +31,7 @@ function getFieldName(path: string): string { /** * Lints a parsed Rulesets document by validating its frontmatter. * For v0.1.0, this performs basic schema validation on the frontmatter. - * + * * @param parsedDoc - The parsed document to lint * @param config - Optional linter configuration * @returns A promise that resolves to an array of lint results @@ -40,10 +40,7 @@ function getFieldName(path: string): string { // :M: v0.1.0: Validates presence and types of frontmatter fields // :M: todo(v0.2.0): Add validation for stem properties // :M: todo(v0.3.0): Add validation for variables and imports -export async function lint( - parsedDoc: ParsedDoc, - config: LinterConfig = {}, -): Promise { +export async function lint(parsedDoc: ParsedDoc, config: LinterConfig = {}): Promise { const results: LintResult[] = []; const { frontmatter } = parsedDoc.source; @@ -54,7 +51,7 @@ export async function lint( message: error.message, line: error.line, column: error.column, - severity: 'error', + severity: "error", }); } } @@ -62,10 +59,11 @@ export async function lint( // If no frontmatter, warn if (!frontmatter) { results.push({ - message: 'No frontmatter found. Consider adding frontmatter with rulesets version and metadata.', + message: + "No frontmatter found. Consider adding frontmatter with rulesets version and metadata.", line: 1, column: 1, - severity: 'warning', + severity: "warning", }); return results; } @@ -74,29 +72,29 @@ export async function lint( if (config.requireRulesetsVersion !== false) { if (!frontmatter.rulesets) { results.push({ - message: `Missing required ${getFieldName('/rulesets')}. Specify the Rulesets version (e.g., rulesets: { version: "0.1.0" }).`, + message: `Missing required ${getFieldName("/rulesets")}. Specify the Rulesets version (e.g., rulesets: { version: "0.1.0" }).`, line: 1, column: 1, - severity: 'error', + severity: "error", }); - } else if (typeof frontmatter.rulesets !== 'object' || !frontmatter.rulesets.version) { + } else if (typeof frontmatter.rulesets !== "object" || !frontmatter.rulesets.version) { results.push({ - message: `Invalid ${getFieldName('/rulesets')}. Expected object with version property, got ${typeof frontmatter.rulesets}.`, + message: `Invalid ${getFieldName("/rulesets")}. Expected object with version property, got ${typeof frontmatter.rulesets}.`, line: 1, column: 1, - severity: 'error', + severity: "error", }); } } // Check destinations if specified if (frontmatter.destinations) { - if (typeof frontmatter.destinations !== 'object' || Array.isArray(frontmatter.destinations)) { + if (typeof frontmatter.destinations !== "object" || Array.isArray(frontmatter.destinations)) { results.push({ - message: `Invalid ${getFieldName('/destinations')}. Expected an object mapping destination IDs to configuration.`, + message: `Invalid ${getFieldName("/destinations")}. Expected an object mapping destination IDs to configuration.`, line: 1, column: 1, - severity: 'error', + severity: "error", }); } else { // Validate allowed destinations if configured @@ -104,10 +102,10 @@ export async function lint( for (const destId of Object.keys(frontmatter.destinations)) { if (!config.allowedDestinations.includes(destId)) { results.push({ - message: `Unknown destination "${destId}". Allowed destinations: ${config.allowedDestinations.join(', ')}.`, + message: `Unknown destination "${destId}". Allowed destinations: ${config.allowedDestinations.join(", ")}.`, line: 1, column: 1, - severity: 'warning', + severity: "warning", }); } } @@ -118,21 +116,21 @@ export async function lint( // Check for recommended fields if (!frontmatter.title) { results.push({ - message: `Consider adding a ${getFieldName('/title')} to the frontmatter for better documentation.`, + message: `Consider adding a ${getFieldName("/title")} to the frontmatter for better documentation.`, line: 1, column: 1, - severity: 'info', + severity: "info", }); } if (!frontmatter.description) { results.push({ - message: `Consider adding a ${getFieldName('/description')} to the frontmatter for better documentation.`, + message: `Consider adding a ${getFieldName("/description")} to the frontmatter for better documentation.`, line: 1, column: 1, - severity: 'info', + severity: "info", }); } return results; -} \ No newline at end of file +} diff --git a/packages/core/src/parser/__tests__/parser.spec.ts b/packages/core/src/parser/__tests__/parser.spec.ts index 214601c..786a65d 100644 --- a/packages/core/src/parser/__tests__/parser.spec.ts +++ b/packages/core/src/parser/__tests__/parser.spec.ts @@ -1,10 +1,10 @@ // TLDR: Unit tests for the Rulesets parser module (mixd-v0) -import { describe, it, expect } from 'vitest'; -import { parse } from '../index'; +import { describe, expect, it } from "vitest"; +import { parse } from "../index"; -describe('parser', () => { - describe('parse', () => { - it('should parse a document with frontmatter and body', async () => { +describe("parser", () => { + describe("parse", () => { + it("should parse a document with frontmatter and body", async () => { const content = `--- mixdown: v0 title: Test Rule @@ -21,11 +21,11 @@ This is the body content.`; expect(result.source.content).toBe(content); expect(result.source.frontmatter).toEqual({ - mixdown: 'v0', - title: 'Test Rule', + mixdown: "v0", + title: "Test Rule", destinations: { cursor: { - path: '.cursor/rules/test.mdc', + path: ".cursor/rules/test.mdc", }, }, }); @@ -36,7 +36,7 @@ This is the body content.`; expect(result.errors).toBeUndefined(); }); - it('should parse a document without frontmatter', async () => { + it("should parse a document without frontmatter", async () => { const content = `# Test Content This is a document without frontmatter.`; @@ -48,7 +48,7 @@ This is a document without frontmatter.`; expect(result.errors).toBeUndefined(); }); - it('should handle empty frontmatter', async () => { + it("should handle empty frontmatter", async () => { const content = `--- --- @@ -60,7 +60,7 @@ This is a document without frontmatter.`; expect(result.errors).toBeUndefined(); }); - it('should report error for invalid YAML frontmatter', async () => { + it("should report error for invalid YAML frontmatter", async () => { const content = `--- invalid: yaml: content bad indentation @@ -72,11 +72,11 @@ invalid: yaml: content expect(result.errors).toBeDefined(); expect(result.errors).toHaveLength(1); - expect(result.errors![0].message).toContain('Failed to parse frontmatter YAML'); + expect(result.errors![0].message).toContain("Failed to parse frontmatter YAML"); expect(result.errors![0].line).toBe(1); }); - it('should report error for unclosed frontmatter', async () => { + it("should report error for unclosed frontmatter", async () => { const content = `--- title: Test description: Missing closing delimiter @@ -87,21 +87,21 @@ description: Missing closing delimiter expect(result.errors).toBeDefined(); expect(result.errors).toHaveLength(1); - expect(result.errors![0].message).toContain('Unclosed frontmatter block'); + expect(result.errors![0].message).toContain("Unclosed frontmatter block"); expect(result.errors![0].line).toBe(1); }); - it('should handle empty content', async () => { - const content = ''; + it("should handle empty content", async () => { + const content = ""; const result = await parse(content); - expect(result.source.content).toBe(''); + expect(result.source.content).toBe(""); expect(result.source.frontmatter).toBeUndefined(); expect(result.errors).toBeUndefined(); }); - it('should preserve body content exactly as-is', async () => { + it("should preserve body content exactly as-is", async () => { const content = `--- title: Test --- @@ -112,8 +112,9 @@ This has {{stems}} and {{$variables}} and {{>imports}} that should be preserved. const result = await parse(content); - const expectedBody = '\n# Test Content\n\nThis has {{stems}} and {{$variables}} and {{>imports}} that should be preserved.'; + const expectedBody = + "\n# Test Content\n\nThis has {{stems}} and {{$variables}} and {{>imports}} that should be preserved."; expect(result.source.content).toContain(expectedBody); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/src/parser/index.ts b/packages/core/src/parser/index.ts index e45ad37..808380e 100644 --- a/packages/core/src/parser/index.ts +++ b/packages/core/src/parser/index.ts @@ -1,12 +1,12 @@ // :M: tldr: Parser implementation for Rulesets notation // :M: v0.1.0: Basic frontmatter extraction without marker processing -import * as yaml from 'js-yaml'; -import type { ParsedDoc } from '../interfaces'; +import * as yaml from "js-yaml"; +import type { ParsedDoc } from "../interfaces"; /** * Parses a Rulesets source rules file to extract frontmatter and body content. * For v0.1.0, this is a simple implementation that doesn't process Rulesets markers. - * + * * @param content - The raw markdown content to parse * @returns A promise that resolves to a ParsedDoc */ @@ -15,18 +15,18 @@ import type { ParsedDoc } from '../interfaces'; // :M: todo(v0.2.0): Add support for stem parsing // :M: todo(v0.3.0): Add variable substitution export async function parse(content: string): Promise { - const lines = content.split('\n'); + const lines = content.split("\n"); let frontmatterStart = -1; let frontmatterEnd = -1; let frontmatter: Record = {}; const errors: Array<{ message: string; line?: number; column?: number }> = []; // Check for frontmatter - if (lines[0].trim() === '---') { + if (lines[0].trim() === "---") { frontmatterStart = 0; // Find the closing frontmatter delimiter for (let i = 1; i < lines.length; i++) { - if (lines[i].trim() === '---') { + if (lines[i].trim() === "---") { frontmatterEnd = i; break; } @@ -34,31 +34,34 @@ export async function parse(content: string): Promise { if (frontmatterEnd > 0) { // Extract and parse frontmatter - const frontmatterContent = lines.slice(frontmatterStart + 1, frontmatterEnd).join('\n'); + const frontmatterContent = lines.slice(frontmatterStart + 1, frontmatterEnd).join("\n"); try { - frontmatter = yaml.load(frontmatterContent) as Record || {}; + frontmatter = (yaml.load(frontmatterContent) as Record) || {}; } catch (error) { - let friendlyMessage = 'Invalid YAML syntax in frontmatter. '; - + let friendlyMessage = "Invalid YAML syntax in frontmatter. "; + if (error instanceof Error) { const message = error.message.toLowerCase(); - + // Add user-friendly context based on common YAML errors - if (message.includes('unexpected end')) { - friendlyMessage += 'Make sure all strings are properly quoted and closed.'; - } else if (message.includes('bad indentation')) { - friendlyMessage += 'Check that your indentation is consistent (use spaces, not tabs).'; - } else if (message.includes('duplicate key')) { - friendlyMessage += 'You have duplicate keys in your frontmatter.'; - } else if (message.includes('unexpected token') || message.includes('unexpected character')) { - friendlyMessage += 'Check for special characters that need to be quoted or escaped.'; + if (message.includes("unexpected end")) { + friendlyMessage += "Make sure all strings are properly quoted and closed."; + } else if (message.includes("bad indentation")) { + friendlyMessage += "Check that your indentation is consistent (use spaces, not tabs)."; + } else if (message.includes("duplicate key")) { + friendlyMessage += "You have duplicate keys in your frontmatter."; + } else if ( + message.includes("unexpected token") || + message.includes("unexpected character") + ) { + friendlyMessage += "Check for special characters that need to be quoted or escaped."; } else { friendlyMessage += `Details: ${error.message}`; } } else { - friendlyMessage += 'Please check your frontmatter formatting.'; + friendlyMessage += "Please check your frontmatter formatting."; } - + errors.push({ message: friendlyMessage, line: frontmatterStart + 1, @@ -71,7 +74,7 @@ export async function parse(content: string): Promise { } else { // Unclosed frontmatter errors.push({ - message: 'Unclosed frontmatter block - missing closing ---', + message: "Unclosed frontmatter block - missing closing ---", line: frontmatterStart + 1, column: 1, }); @@ -84,10 +87,10 @@ export async function parse(content: string): Promise { frontmatter: Object.keys(frontmatter).length > 0 ? frontmatter : undefined, }, ast: { - stems: [], // Empty for v0 - no body processing - imports: [], // Empty for v0 - no body processing - variables: [], // Empty for v0 - no body processing - markers: [], // Empty for v0 - no body processing + stems: [], // Empty for v0 - no body processing + imports: [], // Empty for v0 - no body processing + variables: [], // Empty for v0 - no body processing + markers: [], // Empty for v0 - no body processing }, }; @@ -96,4 +99,4 @@ export async function parse(content: string): Promise { } return parsedDoc; -} \ No newline at end of file +} diff --git a/packages/core/tests/integration/e2e.spec.ts b/packages/core/tests/integration/e2e.spec.ts index e3ced48..833c912 100644 --- a/packages/core/tests/integration/e2e.spec.ts +++ b/packages/core/tests/integration/e2e.spec.ts @@ -1,11 +1,12 @@ // TLDR: End-to-end integration tests for Mixdown v0 (mixd-v0) -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import { promises as fs } from 'fs'; -import path from 'path'; -import { runMixdownV0, ConsoleLogger } from '../../src'; + +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { ConsoleLogger, runMixdownV0 } from "../../src"; // Mock fs to avoid actual file I/O in tests -vi.mock('fs', () => ({ +vi.mock("fs", () => ({ promises: { readFile: vi.fn(), mkdir: vi.fn(), @@ -13,15 +14,15 @@ vi.mock('fs', () => ({ }, })); -describe('E2E Integration Tests', () => { +describe("E2E Integration Tests", () => { let mockLogger: ConsoleLogger; - + beforeEach(() => { mockLogger = new ConsoleLogger(); - vi.spyOn(mockLogger, 'info').mockImplementation(() => {}); - vi.spyOn(mockLogger, 'debug').mockImplementation(() => {}); - vi.spyOn(mockLogger, 'warn').mockImplementation(() => {}); - vi.spyOn(mockLogger, 'error').mockImplementation(() => {}); + vi.spyOn(mockLogger, "info").mockImplementation(() => {}); + vi.spyOn(mockLogger, "debug").mockImplementation(() => {}); + vi.spyOn(mockLogger, "warn").mockImplementation(() => {}); + vi.spyOn(mockLogger, "error").mockImplementation(() => {}); vi.clearAllMocks(); }); @@ -29,7 +30,7 @@ describe('E2E Integration Tests', () => { vi.restoreAllMocks(); }); - describe('runMixdownV0', () => { + describe("runMixdownV0", () => { const sampleContent = `--- mixdown: v0 title: Integration Test Rules @@ -47,57 +48,54 @@ destinations: This is a test document with {{stems}} and {{$variables}} that should pass through.`; - it('should complete the full pipeline successfully', async () => { + it("should complete the full pipeline successfully", async () => { // Mock file read vi.mocked(fs.readFile).mockResolvedValueOnce(sampleContent); // Run the pipeline - await runMixdownV0('./test.mix.md', mockLogger); + await runMixdownV0("./test.mix.md", mockLogger); // Verify file was read - expect(fs.readFile).toHaveBeenCalledWith('./test.mix.md', 'utf-8'); + expect(fs.readFile).toHaveBeenCalledWith("./test.mix.md", "utf-8"); // Verify directories were created - expect(fs.mkdir).toHaveBeenCalledWith( - path.dirname(path.resolve('.cursor/rules/test.mdc')), - { recursive: true }, - ); - expect(fs.mkdir).toHaveBeenCalledWith( - path.dirname(path.resolve('.windsurf/rules/test.md')), - { recursive: true }, - ); + expect(fs.mkdir).toHaveBeenCalledWith(path.dirname(path.resolve(".cursor/rules/test.mdc")), { + recursive: true, + }); + expect(fs.mkdir).toHaveBeenCalledWith(path.dirname(path.resolve(".windsurf/rules/test.md")), { + recursive: true, + }); // Verify files were written with correct content - const expectedContent = '# Test Rules\n\nThis is a test document with {{stems}} and {{$variables}} that should pass through.'; + const expectedContent = + "# Test Rules\n\nThis is a test document with {{stems}} and {{$variables}} that should pass through."; expect(fs.writeFile).toHaveBeenCalledWith( - path.resolve('.cursor/rules/test.mdc'), + path.resolve(".cursor/rules/test.mdc"), expectedContent, - 'utf-8', + "utf-8" ); expect(fs.writeFile).toHaveBeenCalledWith( - path.resolve('.windsurf/rules/test.md'), + path.resolve(".windsurf/rules/test.md"), expectedContent, - 'utf-8', + "utf-8" ); // Verify logging - expect(mockLogger.info).toHaveBeenCalledWith('Mixdown v0 processing completed successfully!'); + expect(mockLogger.info).toHaveBeenCalledWith("Mixdown v0 processing completed successfully!"); }); - it('should handle missing frontmatter gracefully', async () => { - const contentWithoutFrontmatter = '# Just Content\n\nNo frontmatter here.'; + it("should handle missing frontmatter gracefully", async () => { + const contentWithoutFrontmatter = "# Just Content\n\nNo frontmatter here."; vi.mocked(fs.readFile).mockResolvedValueOnce(contentWithoutFrontmatter); - await runMixdownV0('./test.mix.md', mockLogger); + await runMixdownV0("./test.mix.md", mockLogger); // Should still process for all destinations expect(fs.writeFile).toHaveBeenCalledTimes(2); // cursor and windsurf - expect(mockLogger.warn).toHaveBeenCalledWith( - expect.stringContaining('No frontmatter found'), - ); + expect(mockLogger.warn).toHaveBeenCalledWith(expect.stringContaining("No frontmatter found")); }); - it('should fail on lint errors', async () => { + it("should fail on lint errors", async () => { const invalidContent = `--- title: Missing mixdown version --- @@ -105,31 +103,31 @@ title: Missing mixdown version # Content`; vi.mocked(fs.readFile).mockResolvedValueOnce(invalidContent); - await expect(runMixdownV0('./test.mix.md', mockLogger)).rejects.toThrow( - 'Linting failed with errors', + await expect(runMixdownV0("./test.mix.md", mockLogger)).rejects.toThrow( + "Linting failed with errors" ); expect(mockLogger.error).toHaveBeenCalledWith( - expect.stringContaining('Missing required Rulesets version declaration'), + expect.stringContaining("Missing required Rulesets version declaration") ); expect(fs.writeFile).not.toHaveBeenCalled(); }); - it('should handle file read errors', async () => { - const error = new Error('File not found'); + it("should handle file read errors", async () => { + const error = new Error("File not found"); vi.mocked(fs.readFile).mockRejectedValueOnce(error); - await expect(runMixdownV0('./nonexistent.mix.md', mockLogger)).rejects.toThrow( - 'File not found', + await expect(runMixdownV0("./nonexistent.mix.md", mockLogger)).rejects.toThrow( + "File not found" ); expect(mockLogger.error).toHaveBeenCalledWith( - 'Failed to read source file: ./nonexistent.mix.md', - error, + "Failed to read source file: ./nonexistent.mix.md", + error ); }); - it('should process only specified destinations', async () => { + it("should process only specified destinations", async () => { const contentWithOneDestination = `--- mixdown: v0 title: Single Destination @@ -141,33 +139,31 @@ destinations: # Single Destination Content`; vi.mocked(fs.readFile).mockResolvedValueOnce(contentWithOneDestination); - await runMixdownV0('./test.mix.md', mockLogger); + await runMixdownV0("./test.mix.md", mockLogger); // Should only write to cursor, not windsurf expect(fs.writeFile).toHaveBeenCalledTimes(1); expect(fs.writeFile).toHaveBeenCalledWith( - path.resolve('.cursor/rules/single.mdc'), + path.resolve(".cursor/rules/single.mdc"), expect.any(String), - 'utf-8', + "utf-8" ); }); - it('should handle plugin write errors', async () => { + it("should handle plugin write errors", async () => { vi.mocked(fs.readFile).mockResolvedValueOnce(sampleContent); - const writeError = new Error('Permission denied'); + const writeError = new Error("Permission denied"); vi.mocked(fs.writeFile).mockRejectedValueOnce(writeError); - await expect(runMixdownV0('./test.mix.md', mockLogger)).rejects.toThrow( - 'Permission denied', - ); + await expect(runMixdownV0("./test.mix.md", mockLogger)).rejects.toThrow("Permission denied"); expect(mockLogger.error).toHaveBeenCalledWith( - expect.stringContaining('Failed to write cursor output'), - writeError, + expect.stringContaining("Failed to write cursor output"), + writeError ); }); - it('should preserve Mixdown markers in output', async () => { + it("should preserve Mixdown markers in output", async () => { const contentWithMarkers = `--- mixdown: v0 title: Markers Test @@ -182,7 +178,7 @@ These are instructions that should be preserved. Variable: {{$myVar}}`; vi.mocked(fs.readFile).mockResolvedValueOnce(contentWithMarkers); - await runMixdownV0('./test.mix.md', mockLogger); + await runMixdownV0("./test.mix.md", mockLogger); const expectedOutput = `{{instructions}} These are instructions that should be preserved. @@ -192,14 +188,10 @@ These are instructions that should be preserved. Variable: {{$myVar}}`; - expect(fs.writeFile).toHaveBeenCalledWith( - expect.any(String), - expectedOutput, - 'utf-8', - ); + expect(fs.writeFile).toHaveBeenCalledWith(expect.any(String), expectedOutput, "utf-8"); }); - it('should use default paths when not specified', async () => { + it("should use default paths when not specified", async () => { const minimalContent = `--- mixdown: v0 --- @@ -207,19 +199,19 @@ mixdown: v0 # Content`; vi.mocked(fs.readFile).mockResolvedValueOnce(minimalContent); - await runMixdownV0('./test.mix.md', mockLogger); + await runMixdownV0("./test.mix.md", mockLogger); // Should use default paths expect(fs.writeFile).toHaveBeenCalledWith( - path.resolve('.mixdown/dist/cursor/my-rules.md'), + path.resolve(".mixdown/dist/cursor/my-rules.md"), expect.any(String), - 'utf-8', + "utf-8" ); expect(fs.writeFile).toHaveBeenCalledWith( - path.resolve('.mixdown/dist/windsurf/my-rules.md'), + path.resolve(".mixdown/dist/windsurf/my-rules.md"), expect.any(String), - 'utf-8', + "utf-8" ); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 9869533..3a32990 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -9,4 +9,4 @@ }, "include": ["src/**/*.ts"], "exclude": ["node_modules", "dist", "src/**/*.spec.ts", "src/**/__tests__"] -} \ No newline at end of file +} diff --git a/packages/core/tsup.config.ts b/packages/core/tsup.config.ts index 69cddd0..9113a92 100644 --- a/packages/core/tsup.config.ts +++ b/packages/core/tsup.config.ts @@ -1,10 +1,10 @@ // :M: tldr: Build configuration for the @rulesets/core package using tsup // :M: v0.1.0: Basic build config with ESM/CJS output and source maps -import { defineConfig } from 'tsup'; +import { defineConfig } from "tsup"; export default defineConfig({ - entry: ['src/index.ts'], - format: ['esm', 'cjs'], + entry: ["src/index.ts"], + format: ["esm", "cjs"], dts: false, // Temporarily disable, will use tsc directly sourcemap: true, clean: true, @@ -12,10 +12,10 @@ export default defineConfig({ // :M: v0.1.0: Single bundle output without code splitting // :M: todo(v0.2.0): Enable code splitting for better performance shims: true, - external: ['js-yaml'], // Only direct dependencies that consumers must install + external: ["js-yaml"], // Only direct dependencies that consumers must install outExtension({ format }) { return { - js: format === 'esm' ? '.mjs' : '.cjs', + js: format === "esm" ? ".mjs" : ".cjs", }; }, -}); \ No newline at end of file +}); diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index 88c127d..fd7aa8a 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -1,20 +1,15 @@ // Vitest configuration for the @rulesets/core package. Enables global test utilities and coverage reporting. -import { defineConfig } from 'vitest/config'; +import { defineConfig } from "vitest/config"; export default defineConfig({ test: { globals: true, - environment: 'node', // Explicitly set environment + environment: "node", // Explicitly set environment coverage: { - provider: 'v8', // or 'istanbul' - reporter: ['text', 'json', 'html'], - include: ['src/**/*.{ts,tsx,mts,cts}'], - exclude: [ - 'src/interfaces/**', - 'src/**/__tests__/**', - 'src/**/*.spec.ts', - 'src/**/*.test.ts', - ], + provider: "v8", // or 'istanbul' + reporter: ["text", "json", "html"], + include: ["src/**/*.{ts,tsx,mts,cts}"], + exclude: ["src/interfaces/**", "src/**/__tests__/**", "src/**/*.spec.ts", "src/**/*.test.ts"], }, }, -}); \ No newline at end of file +}); diff --git a/tsconfig.base.json b/tsconfig.base.json index b2dcadd..a1f0ce7 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -15,4 +15,4 @@ "types": ["node", "vitest/globals"] }, "exclude": ["node_modules", "**/dist", "**/coverage"] -} \ No newline at end of file +} diff --git a/turbo.json b/turbo.json index 3f3e7d4..54df0fd 100644 --- a/turbo.json +++ b/turbo.json @@ -20,4 +20,4 @@ "dependsOn": ["build", "lint", "test"] } } -} \ No newline at end of file +} From d30b6989de486c9fdca17712fb1e3d6a8044b989 Mon Sep 17 00:00:00 2001 From: Matt Galligan Date: Mon, 1 Sep 2025 14:13:43 -0400 Subject: [PATCH 2/5] fix: resolve linting and TypeScript errors after tooling migration - Fix TypeScript strict mode type errors - Update test files for API changes (runMixdownV0 -> runRulesetsV0) - Change parse and lint functions from async to sync - Add Frontmatter type definition for better type safety - Update tests to match new rulesets frontmatter structure - Configure Biome rules to suppress false positives - Fix all formatting and linting issues --- AGENTS.md | 2 +- CHANGELOG.md | 2 + README.md | 7 ++- biome.json | 9 ++- packages/core/src/compiler/index.ts | 1 + .../core/src/destinations/cursor-plugin.ts | 2 +- packages/core/src/destinations/index.ts | 5 +- .../core/src/destinations/windsurf-plugin.ts | 2 +- packages/core/src/index.ts | 13 ++-- packages/core/src/interfaces/compiled-doc.ts | 6 +- .../core/src/interfaces/destination-plugin.ts | 2 +- packages/core/src/interfaces/frontmatter.ts | 21 +++++++ packages/core/src/interfaces/index.ts | 1 + packages/core/src/interfaces/logger.ts | 15 +++-- .../core/src/linter/__tests__/linter.spec.ts | 62 +++++++++---------- packages/core/src/linter/index.ts | 24 ++++--- .../core/src/parser/__tests__/parser.spec.ts | 30 ++++----- packages/core/src/parser/index.ts | 4 +- packages/core/tests/integration/e2e.spec.ts | 20 +++--- 19 files changed, 136 insertions(+), 92 deletions(-) create mode 100644 packages/core/src/interfaces/frontmatter.ts diff --git a/AGENTS.md b/AGENTS.md index 9329151..a323c86 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -160,4 +160,4 @@ AI agents must consult these documents regularly: - `@docs/agentic/`: Agentic development coordination and agent-specific guides - `@docs/agentic/jules/`: Google Jules integration guides and prompts -This structured approach ensures AI agents can efficiently navigate, understand, and contribute to the Mixdown codebase while maintaining consistency and quality. \ No newline at end of file +This structured approach ensures AI agents can efficiently navigate, understand, and contribute to the Mixdown codebase while maintaining consistency and quality. diff --git a/CHANGELOG.md b/CHANGELOG.md index a12ea01..4d7b023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ### Added + - Added `.mix.md` file extension for source rules files to improve discoverability, search capabilities, and IDE support ### Changed + - Updated terminology throughout the documentation: - "Mix files" → "Source rules" - "Target" → "Destination" diff --git a/README.md b/README.md index 721c58f..1192403 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 📏 Rulesets: A Compiler for AI Rules Files -> **🚀 v0 Release Now Available!** The initial implementation of Rulesets is ready for testing. See [Installation](#installation) to get started. +> **🚀 v0 Release Now Available!** The initial implementation of Rulesets is ready for testing. See [CLI Installation](#cli-installation) to get started. Rulesets simplifies rules management for tools like Cursor, Claude Code, Codex, etc. With Rulesets, you author rules (called "source rules") in previewable Markdown and compile them into compiled rules for each destination (`.cursor/rules.mdc`, `./CLAUDE.md`, `.roo/rules.md`, and more). Think of it as **Terraform for AI rules**: write once, compile for many destinations, your agents, no matter the tool, on the (literal) same page. @@ -151,7 +151,8 @@ async function main() { main(); ``` -### 3. Find your compiled rules at: +### 3. Find your compiled rules at + - `.cursor/rules/standards.mdc` (for Cursor) - `.windsurf/rules/standards.md` (for Windsurf) @@ -231,4 +232,4 @@ Please see our general contributing guidelines for more details. ## References - `docs/project/OVERVIEW.md` – Full Mixdown notation specification. -- `docs/architecture/DECISIONS.md` – Design rationale & deep-dive. \ No newline at end of file +- `docs/architecture/DECISIONS.md` – Design rationale & deep-dive. diff --git a/biome.json b/biome.json index 1717178..04672bd 100644 --- a/biome.json +++ b/biome.json @@ -31,7 +31,14 @@ }, "suspicious": { "noExplicitAny": "off", - "noArrayIndexKey": "off" + "noArrayIndexKey": "off", + "noEmptyBlockStatements": "off" + }, + "performance": { + "noBarrelFile": "off" + }, + "nursery": { + "noUnnecessaryConditions": "off" } } }, diff --git a/packages/core/src/compiler/index.ts b/packages/core/src/compiler/index.ts index b8c389e..376468c 100644 --- a/packages/core/src/compiler/index.ts +++ b/packages/core/src/compiler/index.ts @@ -24,6 +24,7 @@ export function compile( // Handle empty files consistently if (!source.content.trim()) { + // Empty content is valid, proceed with empty body } // Extract the body content (everything after frontmatter) diff --git a/packages/core/src/destinations/cursor-plugin.ts b/packages/core/src/destinations/cursor-plugin.ts index 7e32fcd..86f3bec 100644 --- a/packages/core/src/destinations/cursor-plugin.ts +++ b/packages/core/src/destinations/cursor-plugin.ts @@ -45,7 +45,7 @@ export class CursorPlugin implements DestinationPlugin { logger.info(`Writing Cursor rules to: ${destPath}`); // Determine the output path - const outputPath = config.outputPath || destPath; + const outputPath = (config.outputPath as string | undefined) || destPath; const resolvedPath = path.isAbsolute(outputPath) ? outputPath : path.resolve(outputPath); // Ensure directory exists diff --git a/packages/core/src/destinations/index.ts b/packages/core/src/destinations/index.ts index f291c3c..934f300 100644 --- a/packages/core/src/destinations/index.ts +++ b/packages/core/src/destinations/index.ts @@ -15,5 +15,6 @@ export const destinations: ReadonlyMap = new Map([ ["windsurf", windsurfPlugin], ]); -// Export classes for testing and extension -export { CursorPlugin, WindsurfPlugin }; +// Re-export classes for testing and extension +export { CursorPlugin } from "./cursor-plugin"; +export { WindsurfPlugin } from "./windsurf-plugin"; diff --git a/packages/core/src/destinations/windsurf-plugin.ts b/packages/core/src/destinations/windsurf-plugin.ts index 4831baa..6685227 100644 --- a/packages/core/src/destinations/windsurf-plugin.ts +++ b/packages/core/src/destinations/windsurf-plugin.ts @@ -44,7 +44,7 @@ export class WindsurfPlugin implements DestinationPlugin { const { compiled, destPath, config, logger } = ctx; // Determine the output path - const outputPath = config.outputPath || destPath; + const outputPath = (config.outputPath as string | undefined) || destPath; const resolvedPath = path.resolve(outputPath); logger.info(`Writing Windsurf rules to: ${resolvedPath}`); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8f8085a..22b1ab8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -63,9 +63,9 @@ export async function runRulesetsV0( // Step 2: Parse the content logger.info("Parsing source file..."); - let parsedDoc; + let parsedDoc: ReturnType; try { - parsedDoc = await parse(content); + parsedDoc = parse(content); parsedDoc.source.path = sourceFilePath; if (parsedDoc.errors && parsedDoc.errors.length > 0) { @@ -78,9 +78,9 @@ export async function runRulesetsV0( // Step 3: Lint the parsed document logger.info("Linting document..."); - let lintResults; + let lintResults: ReturnType; try { - lintResults = await lint(parsedDoc, { + lintResults = lint(parsedDoc, { requireRulesetsVersion: true, allowedDestinations: Array.from(destinations.keys()), }); @@ -102,6 +102,9 @@ export async function runRulesetsV0( case "info": logger.info(message); break; + default: + // Exhaustive check for LintSeverity type + break; } } @@ -133,7 +136,7 @@ export async function runRulesetsV0( logger.info(`Processing destination: ${destinationId}`); // Compile for this destination - let compiledDoc; + let compiledDoc: ReturnType; try { compiledDoc = compile(parsedDoc, destinationId, projectConfig); } catch (error) { diff --git a/packages/core/src/interfaces/compiled-doc.ts b/packages/core/src/interfaces/compiled-doc.ts index 061ad9c..2b07276 100644 --- a/packages/core/src/interfaces/compiled-doc.ts +++ b/packages/core/src/interfaces/compiled-doc.ts @@ -1,6 +1,8 @@ // :M: tldr: Defines the CompiledDoc interface for Rulesets // :M: v0.1.0: Minimal interface structure without marker processing +import type { Frontmatter } from "./frontmatter"; + /** * Represents the structure of a parsed Rulesets stem. * For v0.1.0, this will be minimal as stems are not processed from the body. @@ -49,7 +51,7 @@ export type ParsedDoc = { source: { path?: string; // Original source file path, if applicable content: string; // Raw source content - frontmatter?: Record; // Parsed frontmatter data + frontmatter?: Frontmatter; // Parsed frontmatter data }; ast: { // Abstract Syntax Tree - minimal for v0 @@ -70,7 +72,7 @@ export type CompiledDoc = { source: { path?: string; // Original source file path, if applicable content: string; // Raw source content - frontmatter?: Record; // Parsed frontmatter data + frontmatter?: Frontmatter; // Parsed frontmatter data }; /** diff --git a/packages/core/src/interfaces/destination-plugin.ts b/packages/core/src/interfaces/destination-plugin.ts index 1eed54d..91baf0e 100644 --- a/packages/core/src/interfaces/destination-plugin.ts +++ b/packages/core/src/interfaces/destination-plugin.ts @@ -4,7 +4,7 @@ import type { JSONSchema7 } from "json-schema"; import type { CompiledDoc } from "./compiled-doc"; import type { Logger } from "./logger"; -export type { JSONSchema7 }; // Re-export for convenience +export type { JSONSchema7 } from "json-schema"; export type DestinationPlugin = { /** diff --git a/packages/core/src/interfaces/frontmatter.ts b/packages/core/src/interfaces/frontmatter.ts new file mode 100644 index 0000000..a0a67b4 --- /dev/null +++ b/packages/core/src/interfaces/frontmatter.ts @@ -0,0 +1,21 @@ +// :M: tldr: Defines the frontmatter types for Rulesets +// :M: v0.1.0: Basic frontmatter structure for source rules files + +/** + * Frontmatter structure for source rules files + */ +export type Frontmatter = { + title?: string; + description?: string; + version?: string | number; + rulesets?: { + version: string; + [key: string]: unknown; + }; + destinations?: Record; + [key: string]: unknown; +}; \ No newline at end of file diff --git a/packages/core/src/interfaces/index.ts b/packages/core/src/interfaces/index.ts index 6922eb4..d1d63d8 100644 --- a/packages/core/src/interfaces/index.ts +++ b/packages/core/src/interfaces/index.ts @@ -2,4 +2,5 @@ export * from "./compiled-doc"; export * from "./destination-plugin"; +export * from "./frontmatter"; export * from "./logger"; diff --git a/packages/core/src/interfaces/logger.ts b/packages/core/src/interfaces/logger.ts index 95f7c60..bd218a2 100644 --- a/packages/core/src/interfaces/logger.ts +++ b/packages/core/src/interfaces/logger.ts @@ -19,21 +19,28 @@ export type Logger = { export class ConsoleLogger implements Logger { // :M: tldr: Logs a debug message to the console // :M: v0.1.0: Console implementation of debug logging - public debug(_message: string, ..._args: unknown[]): void { + debug(_message: string, ..._args: unknown[]): void { if (process.env.RULESETS_LOG_LEVEL === "debug") { + // Debug logging disabled in v0.1.0 } } // :M: tldr: Logs an informational message to the console // :M: v0.1.0: Console implementation of info logging - public info(_message: string, ..._args: unknown[]): void {} + info(_message: string, ..._args: unknown[]): void { + // Info logging disabled in v0.1.0 + } // :M: tldr: Logs a warning message to the console // :M: v0.1.0: Console implementation of warning logging - public warn(_message: string, ..._args: unknown[]): void {} + warn(_message: string, ..._args: unknown[]): void { + // Warning logging disabled in v0.1.0 + } // :M: tldr: Logs an error message to the console // :M: v0.1.0: Console implementation with stack trace support - public error(message: string | Error, ..._args: unknown[]): void { + error(message: string | Error, ..._args: unknown[]): void { if (message instanceof Error) { + // Error object logging disabled in v0.1.0 } else { + // Error string logging disabled in v0.1.0 } } } diff --git a/packages/core/src/linter/__tests__/linter.spec.ts b/packages/core/src/linter/__tests__/linter.spec.ts index d36df7b..9810937 100644 --- a/packages/core/src/linter/__tests__/linter.spec.ts +++ b/packages/core/src/linter/__tests__/linter.spec.ts @@ -5,12 +5,12 @@ import { lint } from "../index"; describe("linter", () => { describe("lint", () => { - it("should pass a valid document with complete frontmatter", async () => { + it("should pass a valid document with complete frontmatter", () => { const parsedDoc: ParsedDoc = { source: { - content: "---\nmixdown: v0\ntitle: Test\ndescription: Test description\n---\n\n# Content", + content: "---\nrulesets:\n version: v0\ntitle: Test\ndescription: Test description\n---\n\n# Content", frontmatter: { - mixdown: "v0", + rulesets: { version: "v0" }, title: "Test", description: "Test description", }, @@ -23,11 +23,11 @@ describe("linter", () => { }, }; - const results = await lint(parsedDoc); + const results = lint(parsedDoc); expect(results).toHaveLength(0); }); - it("should warn when no frontmatter is present", async () => { + it("should warn when no frontmatter is present", () => { const parsedDoc: ParsedDoc = { source: { content: "# Content without frontmatter", @@ -40,13 +40,13 @@ describe("linter", () => { }, }; - const results = await lint(parsedDoc); + const results = lint(parsedDoc); expect(results).toHaveLength(1); expect(results[0].severity).toBe("warning"); expect(results[0].message).toContain("No frontmatter found"); }); - it("should error when mixdown version is missing", async () => { + it("should error when rulesets version is missing", () => { const parsedDoc: ParsedDoc = { source: { content: "---\ntitle: Test\n---\n\n# Content", @@ -62,20 +62,20 @@ describe("linter", () => { }, }; - const results = await lint(parsedDoc); - const mixdownError = results.find((r) => - r.message.includes('Missing required "mixdown" field') + const results = lint(parsedDoc); + const rulesetsError = results.find((r) => + r.message.includes('Missing required "rulesets" field') ); - expect(mixdownError).toBeDefined(); - expect(mixdownError!.severity).toBe("error"); + expect(rulesetsError).toBeDefined(); + expect(rulesetsError!.severity).toBe("error"); }); - it("should error when mixdown field is not a string", async () => { + it("should error when rulesets field is not a string", () => { const parsedDoc: ParsedDoc = { source: { - content: "---\nmixdown: 123\n---\n\n# Content", + content: "---\nrulesets: 123\n---\n\n# Content", frontmatter: { - mixdown: 123, + rulesets: 123, }, }, ast: { @@ -86,18 +86,18 @@ describe("linter", () => { }, }; - const results = await lint(parsedDoc); - const typeError = results.find((r) => r.message.includes('Invalid "mixdown" field type')); + const results = lint(parsedDoc); + const typeError = results.find((r) => r.message.includes('Invalid "rulesets" field type')); expect(typeError).toBeDefined(); expect(typeError!.severity).toBe("error"); }); - it("should validate destinations structure", async () => { + it("should validate destinations structure", () => { const parsedDoc: ParsedDoc = { source: { - content: '---\nmixdown: v0\ndestinations: ["cursor", "windsurf"]\n---\n\n# Content', + content: '---\nrulesets: v0\ndestinations: ["cursor", "windsurf"]\n---\n\n# Content', frontmatter: { - mixdown: "v0", + rulesets: "v0", destinations: ["cursor", "windsurf"], }, }, @@ -109,19 +109,19 @@ describe("linter", () => { }, }; - const results = await lint(parsedDoc); + const results = lint(parsedDoc); const destError = results.find((r) => r.message.includes('Invalid "destinations" field')); expect(destError).toBeDefined(); expect(destError!.severity).toBe("error"); }); - it("should warn about unknown destinations when configured", async () => { + it("should warn about unknown destinations when configured", () => { const parsedDoc: ParsedDoc = { source: { content: - '---\nmixdown: v0\ndestinations:\n unknown-dest:\n path: "/test"\n---\n\n# Content', + '---\nrulesets: v0\ndestinations:\n unknown-dest:\n path: "/test"\n---\n\n# Content', frontmatter: { - mixdown: "v0", + rulesets: "v0", destinations: { "unknown-dest": { path: "/test" }, }, @@ -135,7 +135,7 @@ describe("linter", () => { }, }; - const results = await lint(parsedDoc, { + const results = lint(parsedDoc, { allowedDestinations: ["cursor", "windsurf"], }); @@ -144,12 +144,12 @@ describe("linter", () => { expect(destWarning!.severity).toBe("warning"); }); - it("should provide info suggestions for missing title and description", async () => { + it("should provide info suggestions for missing title and description", () => { const parsedDoc: ParsedDoc = { source: { - content: "---\nmixdown: v0\n---\n\n# Content", + content: "---\nrulesets: v0\n---\n\n# Content", frontmatter: { - mixdown: "v0", + rulesets: "v0", }, }, ast: { @@ -160,7 +160,7 @@ describe("linter", () => { }, }; - const results = await lint(parsedDoc); + const results = lint(parsedDoc); const titleInfo = results.find((r) => r.message.includes('Consider adding a "title"')); const descInfo = results.find((r) => r.message.includes('Consider adding a "description"')); @@ -170,7 +170,7 @@ describe("linter", () => { expect(descInfo!.severity).toBe("info"); }); - it("should include parsing errors in lint results", async () => { + it("should include parsing errors in lint results", () => { const parsedDoc: ParsedDoc = { source: { content: "---\ninvalid yaml\n---\n\n# Content", @@ -191,7 +191,7 @@ describe("linter", () => { ], }; - const results = await lint(parsedDoc); + const results = lint(parsedDoc); const parseError = results.find((r) => r.message.includes("Failed to parse frontmatter YAML") ); diff --git a/packages/core/src/linter/index.ts b/packages/core/src/linter/index.ts index 555b93a..a4d034e 100644 --- a/packages/core/src/linter/index.ts +++ b/packages/core/src/linter/index.ts @@ -40,7 +40,7 @@ function getFieldName(path: string): string { // :M: v0.1.0: Validates presence and types of frontmatter fields // :M: todo(v0.2.0): Add validation for stem properties // :M: todo(v0.3.0): Add validation for variables and imports -export async function lint(parsedDoc: ParsedDoc, config: LinterConfig = {}): Promise { +export function lint(parsedDoc: ParsedDoc, config: LinterConfig = {}): LintResult[] { const results: LintResult[] = []; const { frontmatter } = parsedDoc.source; @@ -77,7 +77,7 @@ export async function lint(parsedDoc: ParsedDoc, config: LinterConfig = {}): Pro column: 1, severity: "error", }); - } else if (typeof frontmatter.rulesets !== "object" || !frontmatter.rulesets.version) { + } else if (typeof frontmatter.rulesets !== "object" || frontmatter.rulesets === null || !('version' in frontmatter.rulesets)) { results.push({ message: `Invalid ${getFieldName("/rulesets")}. Expected object with version property, got ${typeof frontmatter.rulesets}.`, line: 1, @@ -96,18 +96,16 @@ export async function lint(parsedDoc: ParsedDoc, config: LinterConfig = {}): Pro column: 1, severity: "error", }); - } else { + } else if (config.allowedDestinations && config.allowedDestinations.length > 0) { // Validate allowed destinations if configured - if (config.allowedDestinations && config.allowedDestinations.length > 0) { - for (const destId of Object.keys(frontmatter.destinations)) { - if (!config.allowedDestinations.includes(destId)) { - results.push({ - message: `Unknown destination "${destId}". Allowed destinations: ${config.allowedDestinations.join(", ")}.`, - line: 1, - column: 1, - severity: "warning", - }); - } + for (const destId of Object.keys(frontmatter.destinations)) { + if (!config.allowedDestinations.includes(destId)) { + results.push({ + message: `Unknown destination "${destId}". Allowed destinations: ${config.allowedDestinations.join(", ")}.`, + line: 1, + column: 1, + severity: "warning", + }); } } } diff --git a/packages/core/src/parser/__tests__/parser.spec.ts b/packages/core/src/parser/__tests__/parser.spec.ts index 786a65d..6e5f5e8 100644 --- a/packages/core/src/parser/__tests__/parser.spec.ts +++ b/packages/core/src/parser/__tests__/parser.spec.ts @@ -4,7 +4,7 @@ import { parse } from "../index"; describe("parser", () => { describe("parse", () => { - it("should parse a document with frontmatter and body", async () => { + it("should parse a document with frontmatter and body", () => { const content = `--- mixdown: v0 title: Test Rule @@ -17,7 +17,7 @@ destinations: This is the body content.`; - const result = await parse(content); + const result = parse(content); expect(result.source.content).toBe(content); expect(result.source.frontmatter).toEqual({ @@ -36,31 +36,31 @@ This is the body content.`; expect(result.errors).toBeUndefined(); }); - it("should parse a document without frontmatter", async () => { + it("should parse a document without frontmatter", () => { const content = `# Test Content This is a document without frontmatter.`; - const result = await parse(content); + const result = parse(content); expect(result.source.content).toBe(content); expect(result.source.frontmatter).toBeUndefined(); expect(result.errors).toBeUndefined(); }); - it("should handle empty frontmatter", async () => { + it("should handle empty frontmatter", () => { const content = `--- --- # Test Content`; - const result = await parse(content); + const result = parse(content); expect(result.source.frontmatter).toBeUndefined(); expect(result.errors).toBeUndefined(); }); - it("should report error for invalid YAML frontmatter", async () => { + it("should report error for invalid YAML frontmatter", () => { const content = `--- invalid: yaml: content bad indentation @@ -68,22 +68,22 @@ invalid: yaml: content # Test Content`; - const result = await parse(content); + const result = parse(content); expect(result.errors).toBeDefined(); expect(result.errors).toHaveLength(1); - expect(result.errors![0].message).toContain("Failed to parse frontmatter YAML"); + expect(result.errors![0].message).toContain("Invalid YAML syntax"); expect(result.errors![0].line).toBe(1); }); - it("should report error for unclosed frontmatter", async () => { + it("should report error for unclosed frontmatter", () => { const content = `--- title: Test description: Missing closing delimiter # Test Content`; - const result = await parse(content); + const result = parse(content); expect(result.errors).toBeDefined(); expect(result.errors).toHaveLength(1); @@ -91,17 +91,17 @@ description: Missing closing delimiter expect(result.errors![0].line).toBe(1); }); - it("should handle empty content", async () => { + it("should handle empty content", () => { const content = ""; - const result = await parse(content); + const result = parse(content); expect(result.source.content).toBe(""); expect(result.source.frontmatter).toBeUndefined(); expect(result.errors).toBeUndefined(); }); - it("should preserve body content exactly as-is", async () => { + it("should preserve body content exactly as-is", () => { const content = `--- title: Test --- @@ -110,7 +110,7 @@ title: Test This has {{stems}} and {{$variables}} and {{>imports}} that should be preserved.`; - const result = await parse(content); + const result = parse(content); const expectedBody = "\n# Test Content\n\nThis has {{stems}} and {{$variables}} and {{>imports}} that should be preserved."; diff --git a/packages/core/src/parser/index.ts b/packages/core/src/parser/index.ts index 808380e..5ab932b 100644 --- a/packages/core/src/parser/index.ts +++ b/packages/core/src/parser/index.ts @@ -1,6 +1,6 @@ // :M: tldr: Parser implementation for Rulesets notation // :M: v0.1.0: Basic frontmatter extraction without marker processing -import * as yaml from "js-yaml"; +import yaml from "js-yaml"; import type { ParsedDoc } from "../interfaces"; /** @@ -14,7 +14,7 @@ import type { ParsedDoc } from "../interfaces"; // :M: v0.1.0: Simple YAML frontmatter extraction only // :M: todo(v0.2.0): Add support for stem parsing // :M: todo(v0.3.0): Add variable substitution -export async function parse(content: string): Promise { +export function parse(content: string): ParsedDoc { const lines = content.split("\n"); let frontmatterStart = -1; let frontmatterEnd = -1; diff --git a/packages/core/tests/integration/e2e.spec.ts b/packages/core/tests/integration/e2e.spec.ts index 833c912..d0523a1 100644 --- a/packages/core/tests/integration/e2e.spec.ts +++ b/packages/core/tests/integration/e2e.spec.ts @@ -3,7 +3,7 @@ import { promises as fs } from "node:fs"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { ConsoleLogger, runMixdownV0 } from "../../src"; +import { ConsoleLogger, runRulesetsV0 } from "../../src"; // Mock fs to avoid actual file I/O in tests vi.mock("fs", () => ({ @@ -30,7 +30,7 @@ describe("E2E Integration Tests", () => { vi.restoreAllMocks(); }); - describe("runMixdownV0", () => { + describe("runRulesetsV0", () => { const sampleContent = `--- mixdown: v0 title: Integration Test Rules @@ -53,7 +53,7 @@ This is a test document with {{stems}} and {{$variables}} that should pass throu vi.mocked(fs.readFile).mockResolvedValueOnce(sampleContent); // Run the pipeline - await runMixdownV0("./test.mix.md", mockLogger); + await runRulesetsV0("./test.mix.md", mockLogger); // Verify file was read expect(fs.readFile).toHaveBeenCalledWith("./test.mix.md", "utf-8"); @@ -88,7 +88,7 @@ This is a test document with {{stems}} and {{$variables}} that should pass throu const contentWithoutFrontmatter = "# Just Content\n\nNo frontmatter here."; vi.mocked(fs.readFile).mockResolvedValueOnce(contentWithoutFrontmatter); - await runMixdownV0("./test.mix.md", mockLogger); + await runRulesetsV0("./test.mix.md", mockLogger); // Should still process for all destinations expect(fs.writeFile).toHaveBeenCalledTimes(2); // cursor and windsurf @@ -103,7 +103,7 @@ title: Missing mixdown version # Content`; vi.mocked(fs.readFile).mockResolvedValueOnce(invalidContent); - await expect(runMixdownV0("./test.mix.md", mockLogger)).rejects.toThrow( + await expect(runRulesetsV0("./test.mix.md", mockLogger)).rejects.toThrow( "Linting failed with errors" ); @@ -117,7 +117,7 @@ title: Missing mixdown version const error = new Error("File not found"); vi.mocked(fs.readFile).mockRejectedValueOnce(error); - await expect(runMixdownV0("./nonexistent.mix.md", mockLogger)).rejects.toThrow( + await expect(runRulesetsV0("./nonexistent.mix.md", mockLogger)).rejects.toThrow( "File not found" ); @@ -139,7 +139,7 @@ destinations: # Single Destination Content`; vi.mocked(fs.readFile).mockResolvedValueOnce(contentWithOneDestination); - await runMixdownV0("./test.mix.md", mockLogger); + await runRulesetsV0("./test.mix.md", mockLogger); // Should only write to cursor, not windsurf expect(fs.writeFile).toHaveBeenCalledTimes(1); @@ -155,7 +155,7 @@ destinations: const writeError = new Error("Permission denied"); vi.mocked(fs.writeFile).mockRejectedValueOnce(writeError); - await expect(runMixdownV0("./test.mix.md", mockLogger)).rejects.toThrow("Permission denied"); + await expect(runRulesetsV0("./test.mix.md", mockLogger)).rejects.toThrow("Permission denied"); expect(mockLogger.error).toHaveBeenCalledWith( expect.stringContaining("Failed to write cursor output"), @@ -178,7 +178,7 @@ These are instructions that should be preserved. Variable: {{$myVar}}`; vi.mocked(fs.readFile).mockResolvedValueOnce(contentWithMarkers); - await runMixdownV0("./test.mix.md", mockLogger); + await runRulesetsV0("./test.mix.md", mockLogger); const expectedOutput = `{{instructions}} These are instructions that should be preserved. @@ -199,7 +199,7 @@ mixdown: v0 # Content`; vi.mocked(fs.readFile).mockResolvedValueOnce(minimalContent); - await runMixdownV0("./test.mix.md", mockLogger); + await runRulesetsV0("./test.mix.md", mockLogger); // Should use default paths expect(fs.writeFile).toHaveBeenCalledWith( From e4ca2baaaad0e7a6abb859ad50329321988ada2d Mon Sep 17 00:00:00 2001 From: Matt Galligan Date: Mon, 1 Sep 2025 14:14:37 -0400 Subject: [PATCH 3/5] fix: add typecheck script and root tsconfig --- CHANGELOG.md | 9 +++++++ package.json | 10 +++---- packages/core/package.json | 3 ++- packages/core/src/compiler/index.ts | 11 +++++--- .../__tests__/cursor-plugin.spec.ts | 2 +- .../__tests__/windsurf-plugin.spec.ts | 2 +- .../core/src/destinations/cursor-plugin.ts | 21 ++++++++++----- .../core/src/destinations/windsurf-plugin.ts | 14 +++++++--- packages/core/src/index.ts | 15 ++++++----- packages/core/src/interfaces/frontmatter.ts | 16 +++++++----- packages/core/src/interfaces/logger.ts | 6 +++-- .../core/src/linter/__tests__/linter.spec.ts | 26 ++++++++++++------- tsconfig.json | 8 ++++++ 13 files changed, 95 insertions(+), 48 deletions(-) create mode 100644 tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d7b023..5248c21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,18 @@ +## [0.1.0] - 2025-09-01 + ### Added - Added `.mix.md` file extension for source rules files to improve discoverability, search capabilities, and IDE support ### Changed +- Replaced ESLint with Biome/Ultracite for linting/formatting +- Added Lefthook-managed git hooks +- Configured markdownlint-cli2 for Markdown formatting +- Updated package.json scripts and CI workflow to new lint commands +- Converted parse/lint APIs from async to sync +- Introduced Frontmatter type and updated frontmatter.rulesets structure +- Updated tests to use runRulesetsV0 and new destinations model - Updated terminology throughout the documentation: - "Mix files" → "Source rules" - "Target" → "Destination" diff --git a/package.json b/package.json index 36de34f..b2a29a2 100644 --- a/package.json +++ b/package.json @@ -14,14 +14,14 @@ "lint:fix": "bunx ultracite format .", "lint:md": "markdownlint-cli2 .", "format": "bunx ultracite format .", - "format:check": "bunx ultracite lint", + "format:check": "bunx ultracite lint .", "format:fix": "bunx ultracite format .", "format:md": "markdownlint-cli2 . --fix", - "typecheck": "turbo typecheck", - "check": "bunx ultracite format .", + "typecheck": "tsc --noEmit", + "check": "bun run format:check && bun run lint && bun run lint:md && bun run typecheck", "check:ci": "bunx ultracite lint .", - "check:all": "bun run format && bun run lint && bun run lint:md && bun run typecheck", - "ci": "bun run format && bun run lint && bun run lint:md && bun run typecheck && bun test", + "check:all": "bun run format:check && bun run lint && bun run lint:md && bun run typecheck", + "ci": "bun run format && bun run lint && bun run lint:md && bun run typecheck && bun run test", "changeset": "changeset", "version-packages": "changeset version", "release": "turbo build --filter=@rulesets/core && changeset publish", diff --git a/packages/core/package.json b/packages/core/package.json index d05929b..983f756 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -17,7 +17,7 @@ "dist" ], "scripts": { - "build": "rm -rf dist && tsup && tsc --emitDeclarationOnly", + "build": "rimraf dist && tsup && tsc --emitDeclarationOnly", "dev": "tsup --watch", "test": "vitest run", "test:watch": "vitest watch", @@ -32,6 +32,7 @@ "@types/js-yaml": "^4.0.9", "@types/node": "^18.19.31", "@types/json-schema": "^7.0.15", + "rimraf": "^6.0.1", "tsup": "^8.0.2", "typescript": "^5.4.5", "vitest": "^1.6.0" diff --git a/packages/core/src/compiler/index.ts b/packages/core/src/compiler/index.ts index 376468c..c4c967b 100644 --- a/packages/core/src/compiler/index.ts +++ b/packages/core/src/compiler/index.ts @@ -2,6 +2,9 @@ // :M: v0.1.0: Pass-through implementation without marker processing import type { CompiledDoc, ParsedDoc } from "../interfaces"; +// CRLF-safe line splitting regex +const LINE_SPLIT_REGEX = /\r?\n/; + /** * Compiles a parsed Rulesets document for a specific destination. * For v0.1.0, this is a pass-through implementation that doesn't process markers. @@ -9,7 +12,7 @@ import type { CompiledDoc, ParsedDoc } from "../interfaces"; * @param parsedDoc - The parsed document to compile * @param destinationId - The ID of the destination to compile for * @param projectConfig - Optional project configuration - * @returns A promise that resolves to a CompiledDoc + * @returns CompiledDoc */ // :M: tldr: Compiles parsed document to destination format // :M: v0.1.0: Pass-through implementation without transformation @@ -32,12 +35,12 @@ export function compile( // If there's frontmatter, remove it from the body if (source.frontmatter) { - const lines = source.content.split("\n"); + const lines = source.content.split(LINE_SPLIT_REGEX); let frontmatterEnd = -1; - if (lines[0] === "---") { + if (lines[0].trim() === "---") { for (let i = 1; i < lines.length; i++) { - if (lines[i] === "---") { + if (lines[i].trim() === "---") { frontmatterEnd = i; break; } diff --git a/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts b/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts index 50a042f..d3f972c 100644 --- a/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts +++ b/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts @@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { CompiledDoc, Logger } from "../../interfaces"; import { CursorPlugin } from "../cursor-plugin"; -vi.mock("fs", () => ({ +vi.mock("node:fs", () => ({ promises: { mkdir: vi.fn(), writeFile: vi.fn(), diff --git a/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts b/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts index bca3a7f..681b664 100644 --- a/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts +++ b/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts @@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { CompiledDoc, Logger } from "../../interfaces"; import { WindsurfPlugin } from "../windsurf-plugin"; -vi.mock("fs", () => ({ +vi.mock("node:fs", () => ({ promises: { mkdir: vi.fn(), writeFile: vi.fn(), diff --git a/packages/core/src/destinations/cursor-plugin.ts b/packages/core/src/destinations/cursor-plugin.ts index 86f3bec..a4f23b1 100644 --- a/packages/core/src/destinations/cursor-plugin.ts +++ b/packages/core/src/destinations/cursor-plugin.ts @@ -42,11 +42,16 @@ export class CursorPlugin implements DestinationPlugin { }): Promise { const { compiled, destPath, config, logger } = ctx; - logger.info(`Writing Cursor rules to: ${destPath}`); - - // Determine the output path - const outputPath = (config.outputPath as string | undefined) || destPath; - const resolvedPath = path.isAbsolute(outputPath) ? outputPath : path.resolve(outputPath); + // Determine the output path (support outputPath and path) + const pathFromConfig = + (config as Record).outputPath ?? + (config as Record as any).path; + const outputPath = + typeof pathFromConfig === "string" && pathFromConfig.trim() !== "" + ? pathFromConfig + : destPath; + const resolvedPath = path.resolve(outputPath); + logger.info(`Writing Cursor rules to: ${resolvedPath}`); // Ensure directory exists const dir = path.dirname(resolvedPath); @@ -65,8 +70,10 @@ export class CursorPlugin implements DestinationPlugin { // Log additional context for debugging logger.debug(`Destination: ${compiled.context.destinationId}`); logger.debug(`Config: ${JSON.stringify(config)}`); - if (compiled.output.metadata?.priority) { - logger.debug(`Priority: ${compiled.output.metadata.priority}`); + const priority = + (config as Record as any).priority ?? compiled.output.metadata?.priority; + if (priority) { + logger.debug(`Priority: ${priority}`); } } catch (error) { logger.error(`Failed to write file: ${resolvedPath}`, error); diff --git a/packages/core/src/destinations/windsurf-plugin.ts b/packages/core/src/destinations/windsurf-plugin.ts index 6685227..dcd4826 100644 --- a/packages/core/src/destinations/windsurf-plugin.ts +++ b/packages/core/src/destinations/windsurf-plugin.ts @@ -43,8 +43,14 @@ export class WindsurfPlugin implements DestinationPlugin { }): Promise { const { compiled, destPath, config, logger } = ctx; - // Determine the output path - const outputPath = (config.outputPath as string | undefined) || destPath; + // Determine the output path (support outputPath and path) + const pathFromConfig = + (config as Record).outputPath ?? + (config as Record as any).path; + const outputPath = + typeof pathFromConfig === "string" && pathFromConfig.trim() !== "" + ? pathFromConfig + : destPath; const resolvedPath = path.resolve(outputPath); logger.info(`Writing Windsurf rules to: ${resolvedPath}`); @@ -60,13 +66,13 @@ export class WindsurfPlugin implements DestinationPlugin { // For v0, write the raw content try { - await fs.writeFile(resolvedPath, compiled.output.content, "utf-8"); + await fs.writeFile(resolvedPath, compiled.output.content, "utf8"); logger.info(`Successfully wrote Windsurf rules to: ${resolvedPath}`); // Log additional context for debugging logger.debug(`Destination: ${compiled.context.destinationId}`); logger.debug(`Config: ${JSON.stringify(config)}`); - logger.debug(`Format: ${config.format || "markdown"}`); + logger.debug(`Format: ${((config as any).format as string) ?? "markdown"}`); } catch (error) { logger.error(`Failed to write file: ${resolvedPath}`, error); throw error; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 22b1ab8..67c0cc5 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -102,9 +102,10 @@ export async function runRulesetsV0( case "info": logger.info(message); break; - default: - // Exhaustive check for LintSeverity type + default: { + logger.warn(`Unknown lint severity: ${String(result.severity)}`); break; + } } } @@ -118,9 +119,9 @@ export async function runRulesetsV0( } // Step 4: Determine which destinations to compile for - const frontmatter = parsedDoc.source.frontmatter || {}; - const destinationIds = frontmatter.destinations - ? Object.keys(frontmatter.destinations) + const fm = parsedDoc.source.frontmatter; + const destinationIds = fm?.destinations + ? Object.keys(fm.destinations) : Array.from(destinations.keys()); logger.info(`Compiling for destinations: ${destinationIds.join(", ")}`); @@ -145,9 +146,9 @@ export async function runRulesetsV0( } // Determine output path - const destConfig = frontmatter.destinations?.[destinationId] || {}; + const destConfig = fm?.destinations?.[destinationId] ?? {}; const defaultPath = `.rulesets/dist/${destinationId}/my-rules.md`; - const destPath = destConfig.outputPath || destConfig.path || defaultPath; + const destPath = destConfig.outputPath ?? destConfig.path ?? defaultPath; // Write using the plugin try { diff --git a/packages/core/src/interfaces/frontmatter.ts b/packages/core/src/interfaces/frontmatter.ts index a0a67b4..ae243ba 100644 --- a/packages/core/src/interfaces/frontmatter.ts +++ b/packages/core/src/interfaces/frontmatter.ts @@ -12,10 +12,14 @@ export type Frontmatter = { version: string; [key: string]: unknown; }; - destinations?: Record; + destinations?: Record< + string, + { + outputPath?: string; + /** @deprecated Use `outputPath`; kept for backward compatibility. */ + path?: string; + [key: string]: unknown; + } + >; [key: string]: unknown; -}; \ No newline at end of file +}; diff --git a/packages/core/src/interfaces/logger.ts b/packages/core/src/interfaces/logger.ts index bd218a2..9d15794 100644 --- a/packages/core/src/interfaces/logger.ts +++ b/packages/core/src/interfaces/logger.ts @@ -38,9 +38,11 @@ export class ConsoleLogger implements Logger { // :M: v0.1.0: Console implementation with stack trace support error(message: string | Error, ..._args: unknown[]): void { if (message instanceof Error) { - // Error object logging disabled in v0.1.0 + // biome-ignore lint/suspicious/noConsole: Logger needs to output errors + console.error(message.stack ?? message.message); } else { - // Error string logging disabled in v0.1.0 + // biome-ignore lint/suspicious/noConsole: Logger needs to output errors + console.error(message); } } } diff --git a/packages/core/src/linter/__tests__/linter.spec.ts b/packages/core/src/linter/__tests__/linter.spec.ts index 9810937..5bd666f 100644 --- a/packages/core/src/linter/__tests__/linter.spec.ts +++ b/packages/core/src/linter/__tests__/linter.spec.ts @@ -8,7 +8,8 @@ describe("linter", () => { it("should pass a valid document with complete frontmatter", () => { const parsedDoc: ParsedDoc = { source: { - content: "---\nrulesets:\n version: v0\ntitle: Test\ndescription: Test description\n---\n\n# Content", + content: + "---\nrulesets:\n version: v0\ntitle: Test\ndescription: Test description\n---\n\n# Content", frontmatter: { rulesets: { version: "v0" }, title: "Test", @@ -64,7 +65,7 @@ describe("linter", () => { const results = lint(parsedDoc); const rulesetsError = results.find((r) => - r.message.includes('Missing required "rulesets" field') + r.message.includes("Missing required Rulesets version declaration") ); expect(rulesetsError).toBeDefined(); expect(rulesetsError!.severity).toBe("error"); @@ -87,7 +88,9 @@ describe("linter", () => { }; const results = lint(parsedDoc); - const typeError = results.find((r) => r.message.includes('Invalid "rulesets" field type')); + const typeError = results.find((r) => + r.message.includes("Invalid Rulesets version declaration") + ); expect(typeError).toBeDefined(); expect(typeError!.severity).toBe("error"); }); @@ -95,9 +98,10 @@ describe("linter", () => { it("should validate destinations structure", () => { const parsedDoc: ParsedDoc = { source: { - content: '---\nrulesets: v0\ndestinations: ["cursor", "windsurf"]\n---\n\n# Content', + content: + '---\nrulesets:\n version: v0\ndestinations: ["cursor", "windsurf"]\n---\n\n# Content', frontmatter: { - rulesets: "v0", + rulesets: { version: "v0" }, destinations: ["cursor", "windsurf"], }, }, @@ -110,7 +114,9 @@ describe("linter", () => { }; const results = lint(parsedDoc); - const destError = results.find((r) => r.message.includes('Invalid "destinations" field')); + const destError = results.find((r) => + r.message.includes("Invalid Destination configurations") + ); expect(destError).toBeDefined(); expect(destError!.severity).toBe("error"); }); @@ -119,9 +125,9 @@ describe("linter", () => { const parsedDoc: ParsedDoc = { source: { content: - '---\nrulesets: v0\ndestinations:\n unknown-dest:\n path: "/test"\n---\n\n# Content', + '---\nrulesets:\n version: v0\ndestinations:\n unknown-dest:\n path: "/test"\n---\n\n# Content', frontmatter: { - rulesets: "v0", + rulesets: { version: "v0" }, destinations: { "unknown-dest": { path: "/test" }, }, @@ -147,9 +153,9 @@ describe("linter", () => { it("should provide info suggestions for missing title and description", () => { const parsedDoc: ParsedDoc = { source: { - content: "---\nrulesets: v0\n---\n\n# Content", + content: "---\nrulesets:\n version: v0\n---\n\n# Content", frontmatter: { - rulesets: "v0", + rulesets: { version: "v0" }, }, }, ast: { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e36259e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["packages/*/src/**/*"], + "exclude": ["node_modules", "**/dist", "**/coverage", "**/*.spec.ts", "**/__tests__"] +} \ No newline at end of file From 68947dd28e259354abfc2bca787fb1683c589f4b Mon Sep 17 00:00:00 2001 From: Matt Galligan Date: Mon, 1 Sep 2025 15:16:44 -0400 Subject: [PATCH 4/5] test: fix integration test expectations for API changes - Update cursor plugin to expect { encoding: 'utf8' } object instead of 'utf-8' string - Update windsurf plugin to expect 'utf8' string instead of 'utf-8' string - Update default paths from .mixdown/dist/ to .rulesets/dist/ - Fix node:fs mocking in test files - Update success log message expectation --- .../__tests__/cursor-plugin.spec.ts | 6 +-- .../__tests__/windsurf-plugin.spec.ts | 8 ++-- .../core/src/linter/__tests__/linter.spec.ts | 4 +- packages/core/tests/integration/e2e.spec.ts | 42 ++++++++++++------- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts b/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts index d3f972c..b362892 100644 --- a/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts +++ b/packages/core/src/destinations/__tests__/cursor-plugin.spec.ts @@ -58,8 +58,8 @@ describe("CursorPlugin", () => { describe("write", () => { const mockCompiledDoc: CompiledDoc = { source: { - content: "---\nrulesets: v0\n---\n\n# Test Content", - frontmatter: { rulesets: "v0" }, + content: "---\nrulesets:\n version: v0\n---\n\n# Test Content", + frontmatter: { rulesets: { version: "v0" } }, }, ast: { stems: [], @@ -95,7 +95,7 @@ describe("CursorPlugin", () => { expect(fs.writeFile).toHaveBeenCalledWith(resolvedPath, mockCompiledDoc.output.content, { encoding: "utf8", }); - expect(mockLogger.info).toHaveBeenCalledWith(`Writing Cursor rules to: ${destPath}`); + expect(mockLogger.info).toHaveBeenCalledWith(`Writing Cursor rules to: ${resolvedPath}`); expect(mockLogger.info).toHaveBeenCalledWith( `Successfully wrote Cursor rules to: ${resolvedPath}` ); diff --git a/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts b/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts index 681b664..adc9c76 100644 --- a/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts +++ b/packages/core/src/destinations/__tests__/windsurf-plugin.spec.ts @@ -56,8 +56,8 @@ describe("WindsurfPlugin", () => { describe("write", () => { const mockCompiledDoc: CompiledDoc = { source: { - content: "---\nmixdown: v0\n---\n\n# Test Content", - frontmatter: { mixdown: "v0" }, + content: "---\nrulesets:\n version: v0\n---\n\n# Test Content", + frontmatter: { rulesets: { version: "v0" } }, }, ast: { stems: [], @@ -93,7 +93,7 @@ describe("WindsurfPlugin", () => { expect(fs.writeFile).toHaveBeenCalledWith( resolvedPath, mockCompiledDoc.output.content, - "utf-8" + "utf8" ); expect(mockLogger.info).toHaveBeenCalledWith(`Writing Windsurf rules to: ${resolvedPath}`); expect(mockLogger.info).toHaveBeenCalledWith( @@ -116,7 +116,7 @@ describe("WindsurfPlugin", () => { expect(fs.writeFile).toHaveBeenCalledWith( resolvedPath, mockCompiledDoc.output.content, - "utf-8" + "utf8" ); }); diff --git a/packages/core/src/linter/__tests__/linter.spec.ts b/packages/core/src/linter/__tests__/linter.spec.ts index 5bd666f..1f6f53e 100644 --- a/packages/core/src/linter/__tests__/linter.spec.ts +++ b/packages/core/src/linter/__tests__/linter.spec.ts @@ -167,8 +167,8 @@ describe("linter", () => { }; const results = lint(parsedDoc); - const titleInfo = results.find((r) => r.message.includes('Consider adding a "title"')); - const descInfo = results.find((r) => r.message.includes('Consider adding a "description"')); + const titleInfo = results.find((r) => r.message.includes('Consider adding a Document title')); + const descInfo = results.find((r) => r.message.includes('Consider adding a Document description')); expect(titleInfo).toBeDefined(); expect(titleInfo!.severity).toBe("info"); diff --git a/packages/core/tests/integration/e2e.spec.ts b/packages/core/tests/integration/e2e.spec.ts index d0523a1..91985ec 100644 --- a/packages/core/tests/integration/e2e.spec.ts +++ b/packages/core/tests/integration/e2e.spec.ts @@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ConsoleLogger, runRulesetsV0 } from "../../src"; // Mock fs to avoid actual file I/O in tests -vi.mock("fs", () => ({ +vi.mock("node:fs", () => ({ promises: { readFile: vi.fn(), mkdir: vi.fn(), @@ -32,7 +32,8 @@ describe("E2E Integration Tests", () => { describe("runRulesetsV0", () => { const sampleContent = `--- -mixdown: v0 +rulesets: + version: v0 title: Integration Test Rules description: Testing the full pipeline destinations: @@ -72,16 +73,18 @@ This is a test document with {{stems}} and {{$variables}} that should pass throu expect(fs.writeFile).toHaveBeenCalledWith( path.resolve(".cursor/rules/test.mdc"), expectedContent, - "utf-8" + { + encoding: "utf8", + } ); expect(fs.writeFile).toHaveBeenCalledWith( path.resolve(".windsurf/rules/test.md"), expectedContent, - "utf-8" + "utf8" ); // Verify logging - expect(mockLogger.info).toHaveBeenCalledWith("Mixdown v0 processing completed successfully!"); + expect(mockLogger.info).toHaveBeenCalledWith("Rulesets v0.1.0 processing completed successfully!"); }); it("should handle missing frontmatter gracefully", async () => { @@ -129,7 +132,8 @@ title: Missing mixdown version it("should process only specified destinations", async () => { const contentWithOneDestination = `--- -mixdown: v0 +rulesets: + version: v0 title: Single Destination destinations: cursor: @@ -146,7 +150,9 @@ destinations: expect(fs.writeFile).toHaveBeenCalledWith( path.resolve(".cursor/rules/single.mdc"), expect.any(String), - "utf-8" + { + encoding: "utf8", + } ); }); @@ -165,7 +171,8 @@ destinations: it("should preserve Mixdown markers in output", async () => { const contentWithMarkers = `--- -mixdown: v0 +rulesets: + version: v0 title: Markers Test --- @@ -188,12 +195,17 @@ These are instructions that should be preserved. Variable: {{$myVar}}`; - expect(fs.writeFile).toHaveBeenCalledWith(expect.any(String), expectedOutput, "utf-8"); + expect(fs.writeFile).toHaveBeenCalledWith( + expect.any(String), + expectedOutput, + expect.objectContaining({ encoding: "utf8" }) + ); }); it("should use default paths when not specified", async () => { const minimalContent = `--- -mixdown: v0 +rulesets: + version: v0 --- # Content`; @@ -203,14 +215,16 @@ mixdown: v0 // Should use default paths expect(fs.writeFile).toHaveBeenCalledWith( - path.resolve(".mixdown/dist/cursor/my-rules.md"), + path.resolve(".rulesets/dist/cursor/my-rules.md"), expect.any(String), - "utf-8" + { + encoding: "utf8", + } ); expect(fs.writeFile).toHaveBeenCalledWith( - path.resolve(".mixdown/dist/windsurf/my-rules.md"), + path.resolve(".rulesets/dist/windsurf/my-rules.md"), expect.any(String), - "utf-8" + "utf8" ); }); }); From ac40457849a191761834861e6a4ec2194773227e Mon Sep 17 00:00:00 2001 From: Matt Galligan Date: Mon, 1 Sep 2025 15:17:22 -0400 Subject: [PATCH 5/5] style: apply ultracite formatting fixes --- packages/core/src/linter/__tests__/linter.spec.ts | 6 ++++-- packages/core/src/linter/index.ts | 6 +++++- packages/core/tests/integration/e2e.spec.ts | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/core/src/linter/__tests__/linter.spec.ts b/packages/core/src/linter/__tests__/linter.spec.ts index 1f6f53e..ea7a8f8 100644 --- a/packages/core/src/linter/__tests__/linter.spec.ts +++ b/packages/core/src/linter/__tests__/linter.spec.ts @@ -167,8 +167,10 @@ describe("linter", () => { }; const results = lint(parsedDoc); - const titleInfo = results.find((r) => r.message.includes('Consider adding a Document title')); - const descInfo = results.find((r) => r.message.includes('Consider adding a Document description')); + const titleInfo = results.find((r) => r.message.includes("Consider adding a Document title")); + const descInfo = results.find((r) => + r.message.includes("Consider adding a Document description") + ); expect(titleInfo).toBeDefined(); expect(titleInfo!.severity).toBe("info"); diff --git a/packages/core/src/linter/index.ts b/packages/core/src/linter/index.ts index a4d034e..d62b2f9 100644 --- a/packages/core/src/linter/index.ts +++ b/packages/core/src/linter/index.ts @@ -77,7 +77,11 @@ export function lint(parsedDoc: ParsedDoc, config: LinterConfig = {}): LintResul column: 1, severity: "error", }); - } else if (typeof frontmatter.rulesets !== "object" || frontmatter.rulesets === null || !('version' in frontmatter.rulesets)) { + } else if ( + typeof frontmatter.rulesets !== "object" || + frontmatter.rulesets === null || + !("version" in frontmatter.rulesets) + ) { results.push({ message: `Invalid ${getFieldName("/rulesets")}. Expected object with version property, got ${typeof frontmatter.rulesets}.`, line: 1, diff --git a/packages/core/tests/integration/e2e.spec.ts b/packages/core/tests/integration/e2e.spec.ts index 91985ec..222986a 100644 --- a/packages/core/tests/integration/e2e.spec.ts +++ b/packages/core/tests/integration/e2e.spec.ts @@ -84,7 +84,9 @@ This is a test document with {{stems}} and {{$variables}} that should pass throu ); // Verify logging - expect(mockLogger.info).toHaveBeenCalledWith("Rulesets v0.1.0 processing completed successfully!"); + expect(mockLogger.info).toHaveBeenCalledWith( + "Rulesets v0.1.0 processing completed successfully!" + ); }); it("should handle missing frontmatter gracefully", async () => {