diff --git a/.cursor/worktrees.json b/.cursor/worktrees.json new file mode 100644 index 0000000000..eebba6f0e3 --- /dev/null +++ b/.cursor/worktrees.json @@ -0,0 +1,3 @@ +{ + "setup-worktree": ["yarn"] +} diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index f26feae2b9..211abac62a 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -32,6 +32,16 @@ runs: with: path: ${{ format('{0}/**/node_modules', inputs.working-directory) }} key: yarn-v1-${{ runner.os }}-${{ steps.get-node.outputs.version }}-${{ hashFiles(format('{0}/**/yarn.lock', inputs.working-directory)) }} + - name: Ensure Lage cache directory exists + run: mkdir -p ${{ format('{0}/.lage', inputs.working-directory) }} + shell: bash + - name: Cache Lage + uses: actions/cache@v4 + with: + path: ${{ format('{0}/.lage', inputs.working-directory) }} + key: lage-${{ runner.os }}-${{ github.sha }} + restore-keys: | + lage-${{ runner.os }}- - name: Install working-directory: ${{ inputs.working-directory }} run: yarn --immutable diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 955580ac7a..3fb9db222d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + with: + download-translations: 'false' - name: Build API run: cd packages/api && yarn build - name: Create package tgz @@ -40,6 +42,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + with: + download-translations: 'false' - name: Build CRDT run: cd packages/crdt && yarn build - name: Create package tgz @@ -75,6 +79,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + with: + download-translations: 'false' - name: Build Server run: yarn workspace @actual-app/sync-server build - name: Upload Build diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index c96bcfb4be..36fdb792cd 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -17,6 +17,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + with: + download-translations: 'false' - name: Lint run: yarn lint typecheck: @@ -25,6 +27,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + with: + download-translations: 'false' - name: Typecheck run: yarn typecheck validate-cli: @@ -33,6 +37,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + with: + download-translations: 'false' - name: Build Web run: yarn build:server - name: Check that the built CLI works @@ -43,6 +49,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + with: + download-translations: 'false' - name: Test run: yarn test diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index eb1c982e17..1b795520fb 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -37,6 +37,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + - name: Trust the repository directory + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Run E2E Tests on Netlify URL run: yarn e2e env: @@ -58,6 +60,8 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment uses: ./.github/actions/setup + - name: Trust the repository directory + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Run Desktop app E2E Tests run: | xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" -- yarn e2e:desktop diff --git a/.gitignore b/.gitignore index 1dbb4c00e1..81005bc8f4 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,6 @@ build/ # .d.ts files aren't type-checked with skipLibCheck set to true *.d.ts + +# Lage cache +.lage/ diff --git a/AGENTS.md b/AGENTS.md index efbde9e2bb..cd3ed469f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,7 +40,28 @@ yarn start:desktop - **ALWAYS run yarn commands from the root directory** - never run them in child workspaces - Use `yarn workspace run ` for workspace-specific tasks -- Include `--watch=false` flag when running unit tests to prevent watch mode +- Tests run once and exit by default (using `vitest --run`) + +### Task Orchestration with Lage + +The project uses **[lage](https://microsoft.github.io/lage/)** (a task runner for JavaScript monorepos) to efficiently run tests and other tasks across multiple workspaces: + +- **Parallel execution**: Runs tests in parallel across workspaces for faster feedback +- **Smart caching**: Caches test results to skip unchanged packages (cached in `.lage/` directory) +- **Dependency awareness**: Understands workspace dependencies and execution order +- **Continues on error**: Uses `--continue` flag to run all packages even if one fails + +**Lage Commands:** + +```bash +# Run all tests across all packages +yarn test # Equivalent to: lage test --continue + +# Run tests without cache (for debugging/CI) +yarn test:debug # Equivalent to: lage test --no-cache --continue +``` + +Configuration is in `lage.config.js` at the project root. ## Architecture & Package Structure @@ -54,8 +75,13 @@ The core application logic that runs on any platform. - Platform-agnostic code - Exports for both browser and node environments - Test commands: + ```bash - yarn workspace loot-core run test --watch=false + # Run all loot-core tests + yarn workspace loot-core run test + + # Or run tests across all packages using lage + yarn test ``` #### 2. **desktop-client** (`packages/desktop-client/` - aliased as `@actual-app/web`) @@ -95,9 +121,16 @@ Public API for programmatic access to Actual. - Node.js API - Designed for integrations and automation - Commands: + ```bash + # Build yarn workspace @actual-app/api build - yarn workspace @actual-app/api test --watch=false + + # Run tests + yarn workspace @actual-app/api test + + # Or use lage to run all tests + yarn test ``` #### 5. **sync-server** (`packages/sync-server/` - aliased as `@actual-app/sync-server`) @@ -157,24 +190,29 @@ When implementing changes: **Unit Tests (Vitest)** +The project uses **lage** for running tests across all workspaces efficiently. + ```bash -# All tests +# Run all tests across all packages (using lage) yarn test -# Specific package -yarn workspace loot-core run test --watch=false +# Run tests without cache (for debugging) +yarn test:debug -# Specific test file -yarn workspace loot-core run test path/to/test.test.ts --watch=false +# Run tests for a specific package +yarn workspace loot-core run test + +# Run a specific test file (watch mode) +yarn workspace loot-core run test path/to/test.test.ts ``` **E2E Tests (Playwright)** ```bash -# Desktop client E2E -yarn workspace @actual-app/web e2e +# Run E2E tests for web +yarn e2e -# Desktop Electron E2E +# Desktop Electron E2E (includes full build) yarn e2e:desktop # Visual regression tests @@ -182,6 +220,9 @@ yarn vrt # Visual regression in Docker (consistent environment) yarn vrt:docker + +# Run E2E tests for a specific package +yarn workspace @actual-app/web e2e ``` **Testing Best Practices:** @@ -329,6 +370,7 @@ describe('ComponentName', () => { ### Configuration Files - `/package.json` - Root workspace configuration, scripts +- `/lage.config.js` - Lage task runner configuration - `/eslint.config.mjs` - ESLint configuration (flat config format) - `/tsconfig.json` - Root TypeScript configuration - `/.cursorignore`, `/.gitignore` - Ignored files @@ -348,6 +390,7 @@ describe('ComponentName', () => { - `packages/*/build/` - Built output - `packages/desktop-client/playwright-report/` - Test reports - `packages/desktop-client/test-results/` - Test results +- `.lage/` - Lage task runner cache (improves test performance) ### Key Source Directories @@ -366,8 +409,11 @@ describe('ComponentName', () => { ### Running Specific Tests ```bash -# Unit test for a specific file in loot-core -yarn workspace loot-core run test src/path/to/file.test.ts --watch=false +# Run all tests across all packages (recommended) +yarn test + +# Unit test for a specific file in loot-core (watch mode) +yarn workspace loot-core run test src/path/to/file.test.ts # E2E test for a specific file yarn workspace @actual-app/web run playwright test accounts.test.ts --browser=chromium @@ -432,6 +478,8 @@ Icons in `packages/component-library/src/icons/` are auto-generated. Don't manua 2. For Vitest: check `vitest.config.ts` or `vitest.web.config.ts` 3. For Playwright: check `playwright.config.ts` 4. Ensure mock minimization - prefer real implementations +5. **Lage cache issues**: Clear cache with `rm -rf .lage` if tests behave unexpectedly +6. **Tests continue on error**: With `--continue` flag, all packages run even if one fails ### Import Resolution Issues @@ -466,8 +514,7 @@ Icons in `packages/component-library/src/icons/` are auto-generated. Don't manua ### Visual Regression Tests (VRT) -- Run with `VRT=true` environment variable -- Snapshots stored per test file +- Snapshots stored per test file in `*-snapshots/` directories - Use Docker for consistent environment: `yarn vrt:docker` ## Additional Resources diff --git a/lage.config.js b/lage.config.js new file mode 100644 index 0000000000..91f000bd9a --- /dev/null +++ b/lage.config.js @@ -0,0 +1,30 @@ +/** @type {import('lage').ConfigOptions} */ +module.exports = { + pipeline: { + test: { + type: 'npmScript', + options: { + outputGlob: [ + 'coverage/**', + '**/test-results/**', + '**/playwright-report/**', + ], + }, + }, + build: { + type: 'npmScript', + cache: true, + options: { + outputGlob: ['lib-dist/**', 'dist/**', 'build/**'], + }, + }, + }, + cacheOptions: { + cacheStorageConfig: { + provider: 'local', + outputGlob: ['lib-dist/**', 'dist/**', 'build/**'], + }, + }, + npmClient: 'yarn', + concurrency: 2, +}; diff --git a/package.json b/package.json index 128eb4c892..33352624ea 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ "build:api": "yarn workspace @actual-app/api build", "generate:i18n": "yarn workspace @actual-app/web generate:i18n", "generate:release-notes": "ts-node ./bin/release-note-generator.ts", - "test": "yarn workspaces foreach --all --parallel --verbose run test", - "test:debug": "yarn workspaces foreach --all --verbose run test", + "test": "lage test --continue", + "test:debug": "lage test --no-cache --continue", "e2e": "yarn workspace @actual-app/web run e2e", "e2e:desktop": "yarn build:desktop --skip-exe-build && yarn workspace desktop-electron e2e", "playwright": "yarn workspace @actual-app/web run playwright", @@ -73,6 +73,7 @@ "globals": "^16.4.0", "html-to-image": "^1.11.13", "husky": "^9.1.7", + "lage": "^2.14.14", "lint-staged": "^16.2.3", "minimatch": "^10.0.3", "node-jq": "^6.3.1", diff --git a/packages/api/package.json b/packages/api/package.json index 4d091670b6..3d237b79aa 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -19,7 +19,7 @@ "build:migrations": "cp migrations/*.sql dist/migrations", "build:default-db": "cp default-db.sqlite dist/", "build": "yarn run clean && yarn run build:app && yarn run build:node && yarn run build:migrations && yarn run build:default-db", - "test": "yarn run build:app && yarn run build:crdt && vitest", + "test": "yarn run build:app && yarn run build:crdt && vitest --run", "clean": "rm -rf dist @types" }, "dependencies": { diff --git a/packages/api/vitest.config.ts b/packages/api/vitest.config.ts index a6d20c9ee5..d63b98f09c 100644 --- a/packages/api/vitest.config.ts +++ b/packages/api/vitest.config.ts @@ -5,5 +5,11 @@ export default { // print only console.error return type === 'stderr'; }, + poolOptions: { + threads: { + maxThreads: 2, + minThreads: 1, + }, + }, }, }; diff --git a/packages/ci-actions/package.json b/packages/ci-actions/package.json index 48c5e68a2b..c8fa9f104a 100644 --- a/packages/ci-actions/package.json +++ b/packages/ci-actions/package.json @@ -6,6 +6,6 @@ "vitest": "^3.2.4" }, "scripts": { - "test": "vitest" + "test": "vitest --run" } } diff --git a/packages/ci-actions/vitest.config.mts b/packages/ci-actions/vitest.config.mts index 81a9c30fa1..be77c2468c 100644 --- a/packages/ci-actions/vitest.config.mts +++ b/packages/ci-actions/vitest.config.mts @@ -5,5 +5,10 @@ export default defineConfig({ globals: true, include: ['src/**/*.test.(js|jsx|ts|tsx)'], environment: 'node', + poolOptions: { + threads: { + singleThread: true, + }, + }, }, }); diff --git a/packages/component-library/package.json b/packages/component-library/package.json index 5dad5b6fca..c724a2eebd 100644 --- a/packages/component-library/package.json +++ b/packages/component-library/package.json @@ -54,6 +54,6 @@ "scripts": { "generate:icons": "rm src/icons/*/*.tsx; cd src/icons && svgr --template template.ts --index-template index-template.ts --typescript --expand-props start -d . .", "test": "npm-run-all -cp 'test:*'", - "test:web": "ENV=web vitest -c vitest.web.config.ts" + "test:web": "ENV=web vitest --run -c vitest.web.config.ts" } } diff --git a/packages/component-library/vitest.web.config.ts b/packages/component-library/vitest.web.config.ts index 1b2a9ee7c7..e89bdeb59d 100644 --- a/packages/component-library/vitest.web.config.ts +++ b/packages/component-library/vitest.web.config.ts @@ -21,6 +21,12 @@ export default defineConfig({ environment: 'jsdom', globals: true, include: ['src/**/*.web.test.(js|jsx|ts|tsx)'], + poolOptions: { + threads: { + maxThreads: 2, + minThreads: 1, + }, + }, }, resolve: { alias: [ diff --git a/packages/crdt/package.json b/packages/crdt/package.json index 49f1843a2c..3e14603a71 100644 --- a/packages/crdt/package.json +++ b/packages/crdt/package.json @@ -12,7 +12,7 @@ "build:node": "tsc --p tsconfig.dist.json", "proto:generate": "./bin/generate-proto", "build": "rm -rf dist && yarn run build:node", - "test": "vitest --globals" + "test": "vitest --run --globals" }, "dependencies": { "google-protobuf": "^3.21.4", diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json index f3cbc0a93b..c2ed9c3a28 100644 --- a/packages/desktop-client/package.json +++ b/packages/desktop-client/package.json @@ -84,7 +84,7 @@ "build": "vite build", "build:browser": "cross-env ./bin/build-browser", "generate:i18n": "i18next", - "test": "vitest", + "test": "vitest --run", "e2e": "npx playwright test --browser=chromium", "vrt": "cross-env VRT=true npx playwright test --browser=chromium", "playwright": "playwright" diff --git a/packages/desktop-client/src/i18n.test.ts b/packages/desktop-client/src/i18n.test.ts index cb03332440..c5deb1120e 100644 --- a/packages/desktop-client/src/i18n.test.ts +++ b/packages/desktop-client/src/i18n.test.ts @@ -13,6 +13,14 @@ vi.mock('i18next', () => { }; }); +vi.mock('./languages', () => ({ + languages: { + '/locale/en.json': vi.fn(), + '/locale/uk.json': vi.fn(), + '/locale/pt-BR.json': vi.fn(), + }, +})); + vi.hoisted(vi.resetModules); describe('setI18NextLanguage', () => { diff --git a/packages/desktop-client/src/i18n.ts b/packages/desktop-client/src/i18n.ts index 3178665729..f8565febb4 100644 --- a/packages/desktop-client/src/i18n.ts +++ b/packages/desktop-client/src/i18n.ts @@ -5,7 +5,7 @@ import resourcesToBackend from 'i18next-resources-to-backend'; import * as Platform from 'loot-core/shared/platform'; -const languages = import.meta.glob(['/locale/*.json', '!/locale/*_old.json']); +import { languages } from './languages'; export const availableLanguages = Object.keys(languages).map( path => path.split('/')[2].split('.')[0], diff --git a/packages/desktop-client/src/languages.ts b/packages/desktop-client/src/languages.ts new file mode 100644 index 0000000000..084895d4cd --- /dev/null +++ b/packages/desktop-client/src/languages.ts @@ -0,0 +1,4 @@ +export const languages = import.meta.glob([ + '/locale/*.json', + '!/locale/*_old.json', +]); diff --git a/packages/desktop-client/vite.config.mts b/packages/desktop-client/vite.config.mts index d51a281eef..ef6b4715dd 100644 --- a/packages/desktop-client/vite.config.mts +++ b/packages/desktop-client/vite.config.mts @@ -219,6 +219,12 @@ export default defineConfig(async ({ mode }) => { // print only console.error return type === 'stderr'; }, + poolOptions: { + threads: { + maxThreads: 2, + minThreads: 1, + }, + }, }, }; }); diff --git a/packages/eslint-plugin-actual/package.json b/packages/eslint-plugin-actual/package.json index 9e15d91a96..525a52e46d 100644 --- a/packages/eslint-plugin-actual/package.json +++ b/packages/eslint-plugin-actual/package.json @@ -5,7 +5,7 @@ "exports": "./lib/index.js", "type": "commonjs", "scripts": { - "test": "vitest" + "test": "vitest --run" }, "dependencies": { "requireindex": "^1.2.0" diff --git a/packages/loot-core/package.json b/packages/loot-core/package.json index e59ea118fa..d07ef49179 100644 --- a/packages/loot-core/package.json +++ b/packages/loot-core/package.json @@ -11,8 +11,8 @@ "watch:browser": "cross-env NODE_ENV=development ./bin/build-browser", "generate:i18n": "i18next", "test": "npm-run-all -cp 'test:*'", - "test:node": "ENV=node vitest", - "test:web": "ENV=web vitest -c vitest.web.config.ts" + "test:node": "ENV=node vitest --run", + "test:web": "ENV=web vitest --run -c vitest.web.config.ts" }, "author": "", "license": "ISC", diff --git a/packages/loot-core/vitest.config.ts b/packages/loot-core/vitest.config.ts index 51fce2cb2b..40916290a6 100644 --- a/packages/loot-core/vitest.config.ts +++ b/packages/loot-core/vitest.config.ts @@ -24,6 +24,12 @@ export default defineConfig({ // print only console.error return type === 'stderr'; }, + poolOptions: { + threads: { + maxThreads: 2, + minThreads: 1, + }, + }, }, resolve: { alias: [ diff --git a/packages/loot-core/vitest.web.config.ts b/packages/loot-core/vitest.web.config.ts index 1b2a9ee7c7..e89bdeb59d 100644 --- a/packages/loot-core/vitest.web.config.ts +++ b/packages/loot-core/vitest.web.config.ts @@ -21,6 +21,12 @@ export default defineConfig({ environment: 'jsdom', globals: true, include: ['src/**/*.web.test.(js|jsx|ts|tsx)'], + poolOptions: { + threads: { + maxThreads: 2, + minThreads: 1, + }, + }, }, resolve: { alias: [ diff --git a/packages/sync-server/package.json b/packages/sync-server/package.json index 4b8ca49ee3..8a2c975e5d 100644 --- a/packages/sync-server/package.json +++ b/packages/sync-server/package.json @@ -17,7 +17,7 @@ "start-monitor": "nodemon --exec 'tsc && node build/app' --ignore './build/**/*' --ext 'ts,js' build/app", "build": "tsc && yarn copy-static-assets", "copy-static-assets": "rm -rf build/src/sql && cp -r src/sql build/src/sql", - "test": "NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --trace-warnings' vitest", + "test": "NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --trace-warnings' vitest --run", "db:migrate": "yarn build && cross-env NODE_ENV=development node build/src/scripts/run-migrations.js up", "db:downgrade": "yarn build && cross-env NODE_ENV=development node build/src/scripts/run-migrations.js down", "db:test-migrate": "yarn build && cross-env NODE_ENV=test node build/src/scripts/run-migrations.js up", diff --git a/packages/sync-server/vitest.config.ts b/packages/sync-server/vitest.config.ts index 284db513c8..dfda37b52a 100644 --- a/packages/sync-server/vitest.config.ts +++ b/packages/sync-server/vitest.config.ts @@ -4,15 +4,13 @@ export default { globalSetup: ['./vitest.globalSetup.js'], globals: true, coverage: { - enabled: true, - include: ['**/*.{js,ts,tsx}'], - exclude: [ - '**/node_modules/**', - '**/dist/**', - '**/build/**', - '**/coverage/**', - ], - reporter: ['html', 'lcov', 'text', 'text-summary'], + enabled: false, + }, + poolOptions: { + threads: { + maxThreads: 2, + minThreads: 1, + }, }, }, }; diff --git a/upcoming-release-notes/5964.md b/upcoming-release-notes/5964.md new file mode 100644 index 0000000000..fd3da2d500 --- /dev/null +++ b/upcoming-release-notes/5964.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Refactor test execution to use lage task runner for improved monorepo test orchestration with parallel execution, smart caching, and better CI performance. diff --git a/yarn.lock b/yarn.lock index 492a43f4c2..75e99a25d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6966,6 +6966,7 @@ __metadata: globals: "npm:^16.4.0" html-to-image: "npm:^1.11.13" husky: "npm:^9.1.7" + lage: "npm:^2.14.14" lint-staged: "npm:^16.2.3" minimatch: "npm:^10.0.3" node-jq: "npm:^6.3.1" @@ -11405,6 +11406,65 @@ __metadata: languageName: node linkType: hard +"glob-hasher-darwin-arm64@npm:1.4.2": + version: 1.4.2 + resolution: "glob-hasher-darwin-arm64@npm:1.4.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"glob-hasher-darwin-x64@npm:1.4.2": + version: 1.4.2 + resolution: "glob-hasher-darwin-x64@npm:1.4.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"glob-hasher-linux-x64-gnu@npm:1.4.2": + version: 1.4.2 + resolution: "glob-hasher-linux-x64-gnu@npm:1.4.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"glob-hasher-win32-arm64-msvc@npm:1.4.2": + version: 1.4.2 + resolution: "glob-hasher-win32-arm64-msvc@npm:1.4.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"glob-hasher-win32-x64-msvc@npm:1.4.2": + version: 1.4.2 + resolution: "glob-hasher-win32-x64-msvc@npm:1.4.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"glob-hasher@npm:^1.4.2": + version: 1.4.2 + resolution: "glob-hasher@npm:1.4.2" + dependencies: + glob-hasher-darwin-arm64: "npm:1.4.2" + glob-hasher-darwin-x64: "npm:1.4.2" + glob-hasher-linux-x64-gnu: "npm:1.4.2" + glob-hasher-win32-arm64-msvc: "npm:1.4.2" + glob-hasher-win32-x64-msvc: "npm:1.4.2" + dependenciesMeta: + glob-hasher-darwin-arm64: + optional: true + glob-hasher-darwin-x64: + optional: true + glob-hasher-linux-x64-gnu: + optional: true + glob-hasher-win32-arm64-msvc: + optional: true + glob-hasher-win32-x64-msvc: + optional: true + checksum: 10/7d21697e63cc43f6edcec52b88ed0cc877011edaca1446c0fb1fb2efb3b7bd26f9900dfde7a7ab62ee425c6bd8da359c0a3d34997c9916de6b887029ca2995d8 + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -13213,6 +13273,22 @@ __metadata: languageName: node linkType: hard +"lage@npm:^2.14.14": + version: 2.14.14 + resolution: "lage@npm:2.14.14" + dependencies: + fsevents: "npm:~2.3.2" + glob-hasher: "npm:^1.4.2" + dependenciesMeta: + fsevents: + optional: true + bin: + lage: dist/lage.js + lage-server: dist/lage-server.js + checksum: 10/e8539b2740ccdfb0862070b5752912b20be3e5b1f3729e75cfdf62a7b3d8f60edd547c1d4901ae9b441dbf3faa6940e6597c2c019ba943f5cdbc41f4779b41aa + languageName: node + linkType: hard + "language-subtag-registry@npm:^0.3.20": version: 0.3.23 resolution: "language-subtag-registry@npm:0.3.23"