From 0cb58f5e4d4be9f132b3cb630bf765bd047991ce Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 17 Jul 2026 20:46:39 +0000 Subject: [PATCH 1/4] fix: flush class manifest on SSR renderStart/generateBundle transformIndexHtml is client-only, so SSR builds could keep a stale .classy manifest through the first server render. Flush after transforms via renderStart and generateBundle, and write immediately during builds instead of relying on the debounced path. Co-authored-by: Jeremy Butler --- src/index.ts | 102 ++++++++++++++++++++++------ src/tests/index.test.ts | 144 ++++++++++++++++++++++++++++++++++++++++ tasks/todo.md | 54 ++++++--------- 3 files changed, 247 insertions(+), 53 deletions(-) diff --git a/src/index.ts b/src/index.ts index a651c0e..9ed695c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,6 +61,8 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { let ignoredDirectories: string[] = [] let allClassesSet: Set = new Set() let isBuild = false + /** True when this plugin instance is attached to an SSR / server build. */ + let isSSR = false let initialScanComplete = false let projectRoot = process.cwd() let manifestRoot = process.cwd() @@ -119,6 +121,53 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { onWrote: invalidateTailwindCssModules, }) + /** + * Persist the in-memory class set to disk. + * Used after transforms complete (`renderStart` / `generateBundle`) so SSR + * builds — which never run `transformIndexHtml` — still get a fresh manifest + * before the first server render. + */ + function flushManifest(reason: string): void { + if (allClassesSet.size === 0) { + if (debug) + console.log(`🎩 Skipping manifest flush (${reason}): no classes.`) + return + } + + if (debug) { + console.log( + `🎩 Flushing manifest (${reason}${isSSR ? ', SSR' : ''}): ${allClassesSet.size} class(es).`, + ) + } + + writeDirect( + allClassesSet, + outputDir, + outputFileName, + manifestRoot, + ) + } + + /** Prefer an immediate write during builds so debounce cannot leave the file stale. */ + function scheduleManifestWrite(): void { + if (isBuild) { + writeDirect( + allClassesSet, + outputDir, + outputFileName, + manifestRoot, + ) + return + } + + writeDebounced( + allClassesSet, + outputDir, + outputFileName, + manifestRoot, + ) + } + const transformCache: Map = new Map() const fileClassMap: Map> = new Map() const classRefCounts: Map = new Map() @@ -213,6 +262,9 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { configResolved(config) { isBuild = config.command === 'build' + // Classic SSR builds set `build.ssr`. Vite Environment API (Nuxt / Vite 6+) + // exposes server builds via `consumer === 'server'` on the environment later. + isSSR = Boolean(config.build?.ssr) projectRoot = config.root manifestRoot = options.manifestRoot ? path.resolve(options.manifestRoot) @@ -225,7 +277,8 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { } if (debug) { - console.log(`🎩 Running in ${isBuild ? 'build' : 'dev'} mode.`) + const mode = isBuild ? (isSSR ? 'SSR build' : 'build') : 'dev' + console.log(`🎩 Running in ${mode} mode.`) } }, @@ -327,12 +380,7 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { if (classesChanged) { if (debug) console.log('🎩 Classes changed, writing output file.') - writeDebounced( - allClassesSet, - outputDir, - outputFileName, - manifestRoot, - ) + scheduleManifestWrite() } if (!initialScanComplete) { @@ -366,23 +414,37 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { initialScanComplete = true }, - buildEnd() { + /** + * All modules are transformed by `renderStart`. Flush here so SSR builds + * (which never invoke `transformIndexHtml`) write a complete manifest + * before Rollup emits output / the server renders. + */ + renderStart() { if (!isBuild) return - if (allClassesSet.size === 0) { - if (debug) - console.log('🎩 Build ended, no classes found to write.') - return + // Vite Environment API: prefer live environment over configResolved snapshot. + const environment = (this as { environment?: { name?: string, config?: { consumer?: string } } }).environment + if (environment) { + isSSR = environment.name === 'ssr' + || environment.config?.consumer === 'server' + || isSSR } - if (debug) - console.log('🎩 Build ended, writing final output file.') - writeDirect( - allClassesSet, - outputDir, - outputFileName, - manifestRoot, - ) + flushManifest('renderStart') + }, + + /** + * Final SSR-safe write during bundle generation. Content-deduped with + * `renderStart` / `buildEnd` so duplicate flushes are cheap no-ops. + */ + generateBundle() { + if (!isBuild) return + flushManifest('generateBundle') + }, + + buildEnd() { + if (!isBuild) return + flushManifest('buildEnd') }, } diff --git a/src/tests/index.test.ts b/src/tests/index.test.ts index 10ef2c0..fb777d8 100644 --- a/src/tests/index.test.ts +++ b/src/tests/index.test.ts @@ -613,6 +613,150 @@ describe('useClassy plugin', () => { expect(plugin.buildEnd).toBeDefined() }) + it('should expose renderStart and generateBundle for SSR-safe flushes', () => { + const plugin = useClassy({ debug: true }) as Plugin + + expect(typeof plugin.renderStart).toBe('function') + expect(typeof plugin.generateBundle).toBe('function') + }) + + it('should flush the manifest on renderStart during SSR builds', async () => { + const fs = await import('fs') + const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) + const renameSpy = vi.spyOn(fs.default, 'renameSync').mockImplementation(() => undefined) + ;(fs.default.existsSync as unknown as ReturnType).mockReturnValue(false) + ;(fs.default.mkdirSync as unknown as ReturnType).mockImplementation(() => undefined) + + const plugin = useClassy({ + debug: true, + manifestRoot: '/project', + }) as Plugin + + if (plugin.configResolved) { + await plugin.configResolved({ + command: 'build', + root: '/project/app', + build: { ssr: true }, + } as never) + } + + const transform = plugin.transform as ( + this: { addWatchFile: (id: string) => void }, + code: string, + id: string, + ) => { code: string } | null + + transform.call( + { addWatchFile: vi.fn() }, + '
x
', + '/project/app/Component.vue', + ) + + writeSpy.mockClear() + renameSpy.mockClear() + + if (typeof plugin.renderStart === 'function') { + await plugin.renderStart.call({ + environment: { name: 'ssr', config: { consumer: 'server' } }, + } as never) + } + + expect(writeSpy).toHaveBeenCalled() + expect( + writeSpy.mock.calls.some( + ([filePath, content]) => + String(filePath).includes('output.classy.html') + && String(content).includes('hover:text-red-500'), + ), + ).toBe(true) + + writeSpy.mockRestore() + renameSpy.mockRestore() + }) + + it('should flush the manifest on generateBundle during builds', async () => { + const fs = await import('fs') + const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) + ;(fs.default.existsSync as unknown as ReturnType).mockReturnValue(false) + ;(fs.default.mkdirSync as unknown as ReturnType).mockImplementation(() => undefined) + vi.spyOn(fs.default, 'renameSync').mockImplementation(() => undefined) + + const plugin = useClassy({ + debug: true, + manifestRoot: '/project', + }) as Plugin + + if (plugin.configResolved) { + await plugin.configResolved({ + command: 'build', + root: '/project', + build: { ssr: false }, + } as never) + } + + const transform = plugin.transform as ( + this: { addWatchFile: (id: string) => void }, + code: string, + id: string, + ) => { code: string } | null + + transform.call( + { addWatchFile: vi.fn() }, + '
x
', + '/project/Component.vue', + ) + + writeSpy.mockClear() + + if (typeof plugin.generateBundle === 'function') { + await plugin.generateBundle.call( + {} as never, + {} as never, + {} as never, + false, + ) + } + + expect( + writeSpy.mock.calls.some( + ([filePath, content]) => + String(filePath).includes('output.classy.html') + && String(content).includes('focus:ring-2'), + ), + ).toBe(true) + + writeSpy.mockRestore() + }) + + it('should not flush the manifest on renderStart in dev mode', async () => { + const fs = await import('fs') + const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) + + const plugin = useClassy({ debug: true }) as Plugin + + if (plugin.configResolved) { + await plugin.configResolved({ + command: 'serve', + root: '/mock/cwd', + build: {}, + } as never) + } + + writeSpy.mockClear() + + if (typeof plugin.renderStart === 'function') { + await plugin.renderStart.call({} as never) + } + + expect( + writeSpy.mock.calls.some(([filePath]) => + String(filePath).includes('output.classy.html'), + ), + ).toBe(false) + + writeSpy.mockRestore() + }) + it('should handle configResolved hook', () => { const plugin = useClassy({ debug: true }) as Plugin diff --git a/tasks/todo.md b/tasks/todo.md index f0b3e11..56cdf18 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -1,41 +1,29 @@ -# Issue #36: Manifest written too late for Tailwind +# SSR manifest robustness -## Review summary +## Problem -**Valid bug** against published `vite-plugin-useclassy@3.1.0`. +`manifestRoot` correctly places `.classy/` for Nuxt `srcDir`, but the plugin has no +SSR-aware flush hook. `transformIndexHtml` is client-only, so SSR builds can finish +transforms with a stale on-disk manifest before the first server render. -Tailwind v4 reads `.classy/output.classy.html` via `@source` while CSS compiles. -Published 3.1.0 only wrote that file in `buildEnd`, so the first cold `vite build` -missed UseClassy variants (`md:h-40`). A second build worked because the first -left a manifest behind. +## Plan -## Fix (this PR) +- [x] Add shared `flushManifest` helper (write current `allClassesSet` via `writeDirect`) +- [x] Call it from `renderStart` (post-transform, works for client + SSR) +- [x] Call it from `generateBundle` (final SSR-safe write before emit) +- [x] Track SSR via `config.build.ssr` / environment consumer for debug +- [x] Prefer `writeDirect` over `writeDebounced` during builds (no debounce race) +- [x] Tests for the new hooks +- [x] Commit, push, open PR -- [x] Run project/blade scan on `buildStart` in **dev and build** -- [x] Pass instance `writeDirect` into early scan (triggers `onWrote`) -- [x] Invalidate CSS modules after manifest writes in Dev (HMR) -- [x] Allowlist the manifest in `.classy/.gitignore` so Oxide can read `@source` -- [x] Use function-based Vite watch ignore for `.classy/` (avoid HTML full reload) -- [x] Tests for early scan + CSS invalidation + gitignore allowlist -- [x] Commit, push, open PR (#37) +## Review -## Verification +**Fix:** After all module transforms, `renderStart` and `generateBundle` flush the +class manifest to disk. These hooks run for SSR builds (unlike `transformIndexHtml`), +so Nuxt / Vite SSR no longer relies on a client-only path for a fresh manifest. -- Fresh `vite build` with empty `.classy/` includes variant classes in CSS (reproduced on react demo: `md:text-7xl`) -- Published 3.1.0 still fails first build; local fix succeeds -- Unit tests: 179 passed -- CI checks green on PR head +Also uses immediate writes during builds (`scheduleManifestWrite`) so a 200ms +debounce cannot leave `.classy/output.classy.html` stale mid-build. -## Review (post-implementation) - -Verified against reproduction matching issue #36: - -1. **Cold `vite build`** with empty `.classy/`: CSS includes `md:h-40` on the first run. -2. **Dev HMR**: adding `class:hover="text-pink-500"` updates the manifest and the - Tailwind CSS (`?direct`) without a full page reload. -3. Published 3.1.0 still writes only at `buildEnd` and fails the cold build. - -Root causes addressed: -- Manifest was written too late for Tailwind's CSS pass → early `buildStart` scan. -- Dev updates did not refresh Tailwind → `handleHotUpdate` + CSS invalidation. -- Nested `.classy/.gitignore` of `*` blocked Oxide → allowlist the manifest file. +**Verification:** 183 unit tests passed, including new coverage for SSR `renderStart`, +`generateBundle`, and the dev-mode no-op. From 219cbf462887495f4be08b9e9db1181433328a19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Jul 2026 04:51:53 +0000 Subject: [PATCH 2/4] Merge remote-tracking branch 'origin/main' into cursor/ssr-manifest-robustness-184b --- package-lock.json | 887 ++++++++++++++++++++++++---------------------- 1 file changed, 458 insertions(+), 429 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04a1f06..6b0f74e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,13 @@ { "name": "vite-plugin-useclassy", - "version": "3.0.0", + "version": "3.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vite-plugin-useclassy", - "version": "3.0.0", + "version": "3.1.3", "license": "MIT", - "dependencies": { - "wrangler": "^4.59.1" - }, "bin": { "vite-plugin-useclassy": "dist/cli.js" }, @@ -29,9 +26,10 @@ "typescript": "^5.9.3", "typescript-eslint": "^8.62.1", "vite": "^8.1.0", - "vite-plugin-dts": "^3.9.1", + "vite-plugin-dts": "^4.5.4", "vite-plugin-inspect": "^11.4.1", - "vitest": "^3.2.6" + "vitest": "^3.2.6", + "wrangler": "^4.59.1" }, "engines": { "node": ">=22" @@ -133,6 +131,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz", "integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==", + "dev": true, "license": "MIT OR Apache-2.0", "engines": { "node": ">=22.0.0" @@ -142,6 +141,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz", "integrity": "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==", + "dev": true, "license": "MIT OR Apache-2.0", "peerDependencies": { "unenv": "2.0.0-rc.24", @@ -160,6 +160,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -176,6 +177,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -192,6 +194,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -208,6 +211,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -224,6 +228,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -237,6 +242,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -249,6 +255,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -386,6 +393,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -410,6 +418,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -426,6 +435,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -442,6 +452,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -458,6 +469,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -474,6 +486,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -490,6 +503,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -506,6 +520,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -522,6 +537,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -538,6 +554,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -554,6 +571,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -570,6 +588,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -586,6 +605,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -602,6 +622,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -618,6 +639,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -634,6 +656,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -650,6 +673,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -666,6 +690,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -682,6 +707,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -698,6 +724,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -714,6 +741,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -730,6 +758,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -746,6 +775,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -762,6 +792,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -778,6 +809,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -794,6 +826,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -810,6 +843,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1118,6 +1152,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1130,6 +1165,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1152,6 +1188,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1174,6 +1211,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1190,6 +1228,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1206,9 +1245,7 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1225,9 +1262,7 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1244,9 +1279,7 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1263,9 +1296,7 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1282,9 +1313,7 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1301,9 +1330,7 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1320,9 +1347,7 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1339,9 +1364,7 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1358,9 +1381,7 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1383,9 +1404,7 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1408,9 +1427,7 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1433,9 +1450,7 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1458,9 +1473,7 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1483,9 +1496,7 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1508,9 +1519,7 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1533,9 +1542,7 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1558,6 +1565,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { @@ -1577,6 +1585,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1596,6 +1605,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1615,6 +1625,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1670,6 +1681,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1679,6 +1691,7 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1693,95 +1706,64 @@ } }, "node_modules/@microsoft/api-extractor": { - "version": "7.43.0", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.43.0.tgz", - "integrity": "sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==", + "version": "7.58.12", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.58.12.tgz", + "integrity": "sha512-VNpgC/1LaroLbn+UKmwDYspq+9r3EHyj4vJ3qUy/g8vB0rKt+1Wgs7WFj/JGpgxhG8SNyXs1XwYwe7nqkk8IDg==", "dev": true, "license": "MIT", "dependencies": { - "@microsoft/api-extractor-model": "7.28.13", - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "4.0.2", - "@rushstack/rig-package": "0.5.2", - "@rushstack/terminal": "0.10.0", - "@rushstack/ts-command-line": "4.19.1", - "lodash": "~4.17.15", - "minimatch": "~3.0.3", + "@microsoft/api-extractor-model": "7.33.10", + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/rig-package": "0.7.3", + "@rushstack/terminal": "0.24.2", + "@rushstack/ts-command-line": "5.3.12", + "diff": "~8.0.2", + "minimatch": "10.2.3", "resolve": "~1.22.1", - "semver": "~7.5.4", + "semver": "~7.7.4", "source-map": "~0.6.1", - "typescript": "5.4.2" + "typescript": "5.9.3" }, "bin": { "api-extractor": "bin/api-extractor" } }, "node_modules/@microsoft/api-extractor-model": { - "version": "7.28.13", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz", - "integrity": "sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==", + "version": "7.33.10", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.10.tgz", + "integrity": "sha512-uPUK17xGxeQ3av6TN7awp+dTTSTvx8fZC0XFL1gyt7hFapYuxND3XLXH8vkmI7UjfW92oogzFAFqHSifmJROaw==", "dev": true, "license": "MIT", "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "4.0.2" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.23.3" } }, "node_modules/@microsoft/api-extractor/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@microsoft/api-extractor/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -1789,70 +1771,49 @@ "node": ">=10" } }, - "node_modules/@microsoft/api-extractor/node_modules/typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/@microsoft/tsdoc": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", "dev": true, "license": "MIT" }, "node_modules/@microsoft/tsdoc-config": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", - "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", + "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", "dev": true, "license": "MIT", "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "ajv": "~6.12.6", + "@microsoft/tsdoc": "0.16.0", + "ajv": "~8.18.0", "jju": "~1.4.0", - "resolve": "~1.19.0" + "resolve": "~1.22.2" } }, "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.6", @@ -1905,6 +1866,7 @@ "version": "4.1.6", "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", + "dev": true, "license": "MIT", "dependencies": { "kleur": "^4.1.5" @@ -1914,6 +1876,7 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", + "dev": true, "license": "MIT", "dependencies": { "@poppinss/colors": "^4.1.5", @@ -1925,6 +1888,7 @@ "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1937,6 +1901,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", + "dev": true, "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { @@ -2032,9 +1997,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2052,9 +2014,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2072,9 +2031,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2092,9 +2048,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2112,9 +2065,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2132,9 +2082,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2343,9 +2290,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2360,9 +2304,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2377,9 +2318,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2394,9 +2332,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2411,9 +2346,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2428,9 +2360,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2445,9 +2374,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2462,9 +2388,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2479,9 +2402,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2496,9 +2416,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2513,9 +2430,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2530,9 +2444,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2547,9 +2458,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2641,18 +2549,20 @@ ] }, "node_modules/@rushstack/node-core-library": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", - "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", + "version": "5.23.3", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.23.3.tgz", + "integrity": "sha512-f6uuza7Um65bwsIJgf0MRs7IPA5IG+A+zs1AYGQvpZmLjtTGdfHowhQw4kwF0pPhJCrU4UHNhK8Qa6tLygYZCA==", "dev": true, "license": "MIT", "dependencies": { - "fs-extra": "~7.0.1", + "ajv": "~8.20.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.5.4", - "z-schema": "~5.0.2" + "semver": "~7.7.4" }, "peerDependencies": { "@types/node": "*" @@ -2663,28 +2573,51 @@ } } }, - "node_modules/@rushstack/node-core-library/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "engines": { - "node": ">=10" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@rushstack/node-core-library/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@rushstack/node-core-library/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2692,25 +2625,41 @@ "node": ">=10" } }, + "node_modules/@rushstack/problem-matcher": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.2.1.tgz", + "integrity": "sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@rushstack/rig-package": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz", - "integrity": "sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.3.tgz", + "integrity": "sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==", "dev": true, "license": "MIT", "dependencies": { - "resolve": "~1.22.1", - "strip-json-comments": "~3.1.1" + "jju": "~1.4.0", + "resolve": "~1.22.1" } }, "node_modules/@rushstack/terminal": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", - "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.24.2.tgz", + "integrity": "sha512-KB7PpvzDyKMw/RGU3TxOwxTs3OwZ4gq6+WHlTJN/JfQH4ezliNtWIqver78jTaAJyz/ZAAlJGH7a/M1WyFLFSw==", "dev": true, "license": "MIT", "dependencies": { - "@rushstack/node-core-library": "4.0.2", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/problem-matcher": "0.2.1", "supports-color": "~8.1.1" }, "peerDependencies": { @@ -2739,13 +2688,13 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", - "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.12.tgz", + "integrity": "sha512-Vg2n24arSf7JvUNga2DMHYTbxnVq9L5OtVCp4Gfr8YC/kmL/bmdR8FQhGcpMzMYNY9Vdw3+TaimG11Sf+z1Tpw==", "dev": true, "license": "MIT", "dependencies": { - "@rushstack/terminal": "0.10.0", + "@rushstack/terminal": "0.24.2", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" @@ -2765,6 +2714,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2777,6 +2727,7 @@ "version": "1.2.17", "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.17.tgz", "integrity": "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==", + "dev": true, "license": "CC0-1.0" }, "node_modules/@stylistic/eslint-plugin": { @@ -3237,45 +3188,43 @@ } }, "node_modules/@volar/language-core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", - "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", "dev": true, "license": "MIT", "dependencies": { - "@volar/source-map": "1.11.1" + "@volar/source-map": "2.4.28" } }, "node_modules/@volar/source-map": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", - "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", "dev": true, - "license": "MIT", - "dependencies": { - "muggle-string": "^0.3.1" - } + "license": "MIT" }, "node_modules/@volar/typescript": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", - "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "1.11.1", - "path-browserify": "^1.0.1" + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" } }, "node_modules/@vue/compiler-core": { - "version": "3.5.39", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.39.tgz", - "integrity": "sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==", + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz", + "integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.7", - "@vue/shared": "3.5.39", + "@vue/shared": "3.5.40", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -3302,32 +3251,42 @@ "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.39", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.39.tgz", - "integrity": "sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==", + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz", + "integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.39", - "@vue/shared": "3.5.39" + "@vue/compiler-core": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" } }, "node_modules/@vue/language-core": { - "version": "1.8.27", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", - "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz", + "integrity": "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "~1.11.1", - "@volar/source-map": "~1.11.1", - "@vue/compiler-dom": "^3.3.0", - "@vue/shared": "^3.3.0", - "computeds": "^0.0.1", + "@volar/language-core": "~2.4.11", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^0.4.9", "minimatch": "^9.0.3", - "muggle-string": "^0.3.1", - "path-browserify": "^1.0.1", - "vue-template-compiler": "^2.7.14" + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" @@ -3346,9 +3305,9 @@ "license": "MIT" }, "node_modules/@vue/language-core/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -3372,9 +3331,9 @@ } }, "node_modules/@vue/shared": { - "version": "3.5.39", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.39.tgz", - "integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==", + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz", + "integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==", "dev": true, "license": "MIT" }, @@ -3428,6 +3387,55 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/alien-signals": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.14.tgz", + "integrity": "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -3560,6 +3568,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", + "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { @@ -3809,21 +3818,10 @@ "dev": true, "license": "MIT" }, - "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/computeds": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", - "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", "dev": true, "license": "MIT" }, @@ -3862,6 +3860,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -4031,11 +4030,22 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" } }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -4080,6 +4090,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" @@ -4106,6 +4117,7 @@ "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -4384,6 +4396,13 @@ "node": ">=12.0.0" } }, + "node_modules/exsolve": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.0.tgz", + "integrity": "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4405,6 +4424,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -4512,18 +4548,18 @@ } }, "node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz", + "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" } }, "node_modules/fs-minipass": { @@ -4556,6 +4592,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5163,11 +5200,14 @@ "license": "MIT" }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "dev": true, "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -5186,6 +5226,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5355,9 +5396,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5379,9 +5417,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5403,9 +5438,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5427,9 +5459,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5485,44 +5514,58 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "node_modules/local-pkg/node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", "dev": true, "license": "MIT" }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "node_modules/local-pkg/node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5587,6 +5630,7 @@ "version": "4.20260630.0", "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260630.0.tgz", "integrity": "sha512-lyRplDrSJJWVpzSSQPBSQtNmUuxScCZyOOkXFs37uSbdTfWRDDmw6DyFKVS2s1eYtA/i4u2xR/0FyPIsTl/HJw==", + "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -5710,9 +5754,9 @@ "license": "MIT" }, "node_modules/muggle-string": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", - "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", "dev": true, "license": "MIT" }, @@ -5977,12 +6021,14 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, "license": "MIT" }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, "license": "MIT" }, "node_modules/pathval": { @@ -6096,6 +6142,23 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/rc9": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", @@ -6143,6 +6206,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -6305,6 +6378,7 @@ "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6317,6 +6391,7 @@ "version": "0.34.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -6817,6 +6892,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD", "optional": true }, @@ -6882,6 +6958,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "dev": true, "license": "MIT", "engines": { "node": ">=20.18.1" @@ -6891,19 +6968,20 @@ "version": "2.0.0-rc.24", "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", + "dev": true, "license": "MIT", "dependencies": { "pathe": "^2.0.3" } }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/unplugin-utils": { @@ -6933,16 +7011,6 @@ "punycode": "^2.1.0" } }, - "node_modules/validator": { - "version": "13.15.35", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", - "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/vite": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.2.tgz", @@ -7150,22 +7218,21 @@ } }, "node_modules/vite-plugin-dts": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-3.9.1.tgz", - "integrity": "sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz", + "integrity": "sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==", "dev": true, "license": "MIT", "dependencies": { - "@microsoft/api-extractor": "7.43.0", - "@rollup/pluginutils": "^5.1.0", - "@vue/language-core": "^1.8.27", - "debug": "^4.3.4", + "@microsoft/api-extractor": "^7.50.1", + "@rollup/pluginutils": "^5.1.4", + "@volar/typescript": "^2.4.11", + "@vue/language-core": "2.2.0", + "compare-versions": "^6.1.1", + "debug": "^4.4.0", "kolorist": "^1.8.0", - "magic-string": "^0.30.8", - "vue-tsc": "^1.8.27" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" + "local-pkg": "^1.0.0", + "magic-string": "^0.30.17" }, "peerDependencies": { "typescript": "*", @@ -7436,34 +7503,12 @@ } } }, - "node_modules/vue-template-compiler": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", - "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" - } - }, - "node_modules/vue-tsc": { - "version": "1.8.27", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", - "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@volar/typescript": "~1.11.1", - "@vue/language-core": "1.8.27", - "semver": "^7.5.4" - }, - "bin": { - "vue-tsc": "bin/vue-tsc.js" - }, - "peerDependencies": { - "typescript": "*" - } + "license": "MIT" }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", @@ -7573,6 +7618,7 @@ "version": "1.20260630.1", "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260630.1.tgz", "integrity": "sha512-7M0AA4l14hmPGtzQ5YPHyXosIKI/uz3TdcPHeiFDbgb7/0c8ECVMzIaodSV5bZIVhDHL0OlzqITAdPiwAr+dTg==", + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "bin": { @@ -7593,6 +7639,7 @@ "version": "4.106.0", "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.106.0.tgz", "integrity": "sha512-b6EVbsvbmAUY4bUQXT3+f8oFP8x+J5rEa5z3Akeh+6vyKiN4x8+PyZ53DPpnqdxhIihhq/a00Yq5chGJ19QXBQ==", + "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", @@ -7723,6 +7770,7 @@ "version": "8.21.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -7813,6 +7861,7 @@ "version": "4.1.0-beta.10", "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", + "dev": true, "license": "MIT", "dependencies": { "@poppinss/colors": "^4.1.5", @@ -7826,32 +7875,12 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", + "dev": true, "license": "MIT", "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } - }, - "node_modules/z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - }, - "bin": { - "z-schema": "bin/z-schema" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "commander": "^9.4.1" - } } } } From d3dca263778c2853bab1d3d0af893fec548f1b5c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 21 Jul 2026 17:16:50 +0000 Subject: [PATCH 3/4] fix: skip per-module manifest writes during build Defer build-time manifest writes to renderStart/generateBundle/buildEnd instead of sync-writing on every transform. Clarify flush tests around the real writer (via fs) and generateBundle's Rollup signature. Co-authored-by: Jeremy Butler --- src/index.ts | 15 ++++------ src/tests/index.test.ts | 64 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/index.ts b/src/index.ts index c30d6c1..6d3fdfb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -151,17 +151,14 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { ) } - /** Prefer an immediate write during builds so debounce cannot leave the file stale. */ + /** + * Schedule a manifest write after classes change during transform. + * Builds skip here — `renderStart` / `generateBundle` / `buildEnd` flush + * once after all modules are processed, avoiding per-module sync rewrites. + */ function scheduleManifestWrite(): void { - if (isBuild) { - writeDirect( - allClassesSet, - outputDir, - outputFileName, - manifestRoot, - ) + if (isBuild) return - } writeDebounced( allClassesSet, diff --git a/src/tests/index.test.ts b/src/tests/index.test.ts index cde9ec2..b393fe7 100644 --- a/src/tests/index.test.ts +++ b/src/tests/index.test.ts @@ -34,6 +34,9 @@ vi.mock('path', () => ({ })) // Mock utils module +// NOTE: `vi.doMock` is not applied to the static `../index` import (ESM hoisting). +// The plugin closes over the real `createOutputFileWriter`; assert writes via `fs` +// (see scanProjectFiles tests which spy `importActual` for the same reason). vi.doMock('../utils', () => { const writeOutputFileDirect = vi.fn(() => true) const writeOutputFileDebounced = vi.fn() @@ -623,6 +626,8 @@ describe('useClassy plugin', () => { }) it('should flush the manifest on renderStart during SSR builds', async () => { + // Plugin uses real createOutputFileWriter (doMock does not replace the + // static index import); observe the write through the mocked fs module. const fs = await import('fs') const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) const renameSpy = vi.spyOn(fs.default, 'renameSync').mockImplementation(() => undefined) @@ -710,13 +715,16 @@ describe('useClassy plugin', () => { writeSpy.mockClear() + // Rollup/Vite signature: generateBundle(outputOptions, bundle, isWrite) if (typeof plugin.generateBundle === 'function') { - await plugin.generateBundle.call( - {} as never, - {} as never, - {} as never, - false, - ) + const generateBundle = plugin.generateBundle as ( + this: unknown, + outputOptions: object, + bundle: object, + isWrite?: boolean, + ) => void | Promise + + await generateBundle.call({}, {}, {}, false) } expect( @@ -759,6 +767,50 @@ describe('useClassy plugin', () => { writeSpy.mockRestore() }) + it('should not write the manifest from transform during builds', async () => { + const fs = await import('fs') + const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) + ;(fs.default.existsSync as unknown as ReturnType).mockReturnValue(false) + ;(fs.default.mkdirSync as unknown as ReturnType).mockImplementation(() => undefined) + vi.spyOn(fs.default, 'renameSync').mockImplementation(() => undefined) + + const plugin = useClassy({ + debug: true, + manifestRoot: '/project', + }) as Plugin + + if (plugin.configResolved) { + await plugin.configResolved({ + command: 'build', + root: '/project', + build: { ssr: true }, + } as never) + } + + writeSpy.mockClear() + + const transform = plugin.transform as ( + this: { addWatchFile: (id: string) => void }, + code: string, + id: string, + ) => { code: string } | null + + transform.call( + { addWatchFile: vi.fn() }, + '
x
', + '/project/Component.vue', + ) + + // Builds defer writes to renderStart / generateBundle / buildEnd. + expect( + writeSpy.mock.calls.some(([filePath]) => + String(filePath).includes('output.classy.html'), + ), + ).toBe(false) + + writeSpy.mockRestore() + }) + it('should handle configResolved hook', () => { const plugin = useClassy({ debug: true }) as Plugin From dca2cfb4efae527d2dd82506000094ab6467221b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 21 Jul 2026 20:53:17 +0000 Subject: [PATCH 4/4] fix: skip manifest writes from Vite server environments In dual client+SSR setups, only the client instance writes the shared manifest so a smaller server class set cannot overwrite it. Classic build.ssr without an Environment API server consumer still flushes. Co-authored-by: Jeremy Butler --- src/index.ts | 147 +++++++++++++++++++++++++++++----------- src/tests/index.test.ts | 77 +++++++++++++++++++-- 2 files changed, 178 insertions(+), 46 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6d3fdfb..95123d3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -65,6 +65,14 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { let isBuild = false /** True when this plugin instance is attached to an SSR / server build. */ let isSSR = false + /** + * Vite Environment API server consumer (`consumer === 'server'` / `name === 'ssr'`). + * Distinct from classic `build.ssr`: in dual client+SSR environments only the + * client instance should write the shared manifest, or a smaller SSR set can + * overwrite classes the client already flushed. Classic `vite build --ssr` + * alone still writes (no Environment API server consumer). + */ + let isEnvironmentServer = false let initialScanComplete = false let projectRoot = process.cwd() let manifestRoot = process.cwd() @@ -124,13 +132,71 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { onWrote: invalidateTailwindCssModules, }) + type EnvironmentLike = { + name?: string + config?: { consumer?: string } + } + + function detectEnvironmentServer( + config?: { build?: { ssr?: boolean | string }, consumer?: string }, + environment?: EnvironmentLike, + ): boolean { + if (environment?.name === 'ssr' || environment?.config?.consumer === 'server') + return true + // Vite Environment API sets `consumer` on the resolved environment config. + if (config?.consumer === 'server') + return true + return false + } + + function syncEnvironmentFlags( + config?: { build?: { ssr?: boolean | string }, consumer?: string }, + environment?: EnvironmentLike, + ): void { + const envServer = detectEnvironmentServer(config, environment) + if (config) + isSSR = Boolean(config.build?.ssr) || envServer + else if (envServer) + isSSR = true + + if (envServer) + isEnvironmentServer = true + } + + /** + * Write the class manifest unless this instance is the Environment API server + * side of a dual client+SSR setup (would shrink a shared manifest). + * Signature matches `writeDirect` so it can be passed to scan helpers. + */ + function writeManifest( + classes: Set = allClassesSet, + dir: string = outputDir, + fileName: string = outputFileName, + root: string = manifestRoot, + ): boolean { + if (isEnvironmentServer) { + if (debug) + console.log('🎩 Skipping manifest write (Vite server environment).') + return false + } + + return writeDirect(classes, dir, fileName, root) + } + /** * Persist the in-memory class set to disk. - * Used after transforms complete (`renderStart` / `generateBundle`) so SSR - * builds — which never run `transformIndexHtml` — still get a fresh manifest - * before the first server render. + * Used after transforms complete (`renderStart` / `generateBundle`) so builds + * still get a fresh manifest before emit / first server render. + * Skipped for Environment API server instances — the client instance owns the + * shared manifest file. */ function flushManifest(reason: string): void { + if (isEnvironmentServer) { + if (debug) + console.log(`🎩 Skipping manifest flush (${reason}): server environment.`) + return + } + if (allClassesSet.size === 0) { if (debug) console.log(`🎩 Skipping manifest flush (${reason}): no classes.`) @@ -143,21 +209,17 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { ) } - writeDirect( - allClassesSet, - outputDir, - outputFileName, - manifestRoot, - ) + writeManifest() } /** * Schedule a manifest write after classes change during transform. * Builds skip here — `renderStart` / `generateBundle` / `buildEnd` flush * once after all modules are processed, avoiding per-module sync rewrites. + * Environment API server instances never write (client owns the file). */ function scheduleManifestWrite(): void { - if (isBuild) + if (isBuild || isEnvironmentServer) return writeDebounced( @@ -231,7 +293,7 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { outputFileName, manifestRoot, debug, - writeDirect, + writeManifest, ) } @@ -245,7 +307,7 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { outputFileName, debug, manifestRoot, - writeDirect, + writeManifest, ) } @@ -268,9 +330,10 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { configResolved(config) { isBuild = config.command === 'build' - // Classic SSR builds set `build.ssr`. Vite Environment API (Nuxt / Vite 6+) - // exposes server builds via `consumer === 'server'` on the environment later. - isSSR = Boolean(config.build?.ssr) + // Classic SSR: `build.ssr`. Vite Environment API (Nuxt / Vite 6+): + // `consumer === 'server'` — that path skips manifest writes so a dual + // client+SSR setup cannot shrink the shared file. + syncEnvironmentFlags(config as { build?: { ssr?: boolean | string }, consumer?: string }) projectRoot = config.root manifestRoot = options.manifestRoot ? path.resolve(options.manifestRoot) @@ -283,7 +346,9 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { } if (debug) { - const mode = isBuild ? (isSSR ? 'SSR build' : 'build') : 'dev' + const mode = isBuild + ? (isEnvironmentServer ? 'server environment build' : isSSR ? 'SSR build' : 'build') + : 'dev' console.log(`🎩 Running in ${mode} mode.`) } }, @@ -308,19 +373,18 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { outputFileName, debug, manifestRoot, - writeDebounced, + (...args: Parameters) => { + if (isEnvironmentServer) + return + writeDebounced(...args) + }, ) } server.httpServer?.once('listening', () => { if (initialScanComplete && allClassesSet.size > 0) { if (debug) console.log('🎩 Initial write on server ready.') - writeDirect( - allClassesSet, - outputDir, - outputFileName, - manifestRoot, - ) + writeManifest() } }) }, @@ -384,8 +448,13 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { const classesChanged = applyFileClasses(id, fileSpecificClasses) if (classesChanged) { - if (debug) - console.log('🎩 Classes changed, writing output file.') + if (debug) { + console.log( + isBuild || isEnvironmentServer + ? '🎩 Classes changed (manifest write deferred).' + : '🎩 Classes changed, writing output file.', + ) + } scheduleManifestWrite() } @@ -401,6 +470,12 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { }, buildStart() { + // Environment API: `this.environment` is available here even when + // `config.build.ssr` was unset in configResolved. + const environment = (this as { environment?: EnvironmentLike }).environment + if (environment) + syncEnvironmentFlags(undefined, environment) + if (debug) console.log('🎩 Build starting, resetting state.') allClassesSet = new Set() classRefCounts.clear() @@ -421,20 +496,17 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { }, /** - * All modules are transformed by `renderStart`. Flush here so SSR builds - * (which never invoke `transformIndexHtml`) write a complete manifest - * before Rollup emits output / the server renders. + * All modules are transformed by the end of the build phase. Flush here so + * builds write a complete manifest before Rollup emits output. + * Environment API server instances skip — client owns the shared file. */ renderStart() { if (!isBuild) return // Vite Environment API: prefer live environment over configResolved snapshot. - const environment = (this as { environment?: { name?: string, config?: { consumer?: string } } }).environment - if (environment) { - isSSR = environment.name === 'ssr' - || environment.config?.consumer === 'server' - || isSSR - } + const environment = (this as { environment?: EnvironmentLike }).environment + if (environment) + syncEnvironmentFlags(undefined, environment) flushManifest('renderStart') }, @@ -491,12 +563,7 @@ export default function useClassy(options: ClassyOptions = {}): PluginOption { console.log( '🎩 Manual output generation requested via HTTP endpoint.', ) - writeDirect( - allClassesSet, - outputDir, - outputFileName, - manifestRoot, - ) + writeManifest() res.statusCode = 200 res.end(`Output file generated (${allClassesSet.size} classes)`) }, diff --git a/src/tests/index.test.ts b/src/tests/index.test.ts index b393fe7..59e0123 100644 --- a/src/tests/index.test.ts +++ b/src/tests/index.test.ts @@ -625,9 +625,10 @@ describe('useClassy plugin', () => { expect(typeof plugin.generateBundle).toBe('function') }) - it('should flush the manifest on renderStart during SSR builds', async () => { - // Plugin uses real createOutputFileWriter (doMock does not replace the - // static index import); observe the write through the mocked fs module. + it('should flush the manifest on renderStart during classic SSR builds', async () => { + // Classic `build.ssr` (no Environment API server consumer) still owns the + // write — Plugin uses real createOutputFileWriter (doMock does not replace + // the static index import); observe via mocked fs. const fs = await import('fs') const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) const renameSpy = vi.spyOn(fs.default, 'renameSync').mockImplementation(() => undefined) @@ -663,9 +664,8 @@ describe('useClassy plugin', () => { renameSpy.mockClear() if (typeof plugin.renderStart === 'function') { - await plugin.renderStart.call({ - environment: { name: 'ssr', config: { consumer: 'server' } }, - } as never) + // No Environment API server consumer — classic SSR build still flushes. + await plugin.renderStart.call({} as never) } expect(writeSpy).toHaveBeenCalled() @@ -681,6 +681,71 @@ describe('useClassy plugin', () => { renameSpy.mockRestore() }) + it('should not flush the manifest from a Vite server environment', async () => { + // Dual client+SSR: server environment must not overwrite the shared file. + const fs = await import('fs') + const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined) + ;(fs.default.existsSync as unknown as ReturnType).mockReturnValue(false) + ;(fs.default.mkdirSync as unknown as ReturnType).mockImplementation(() => undefined) + vi.spyOn(fs.default, 'renameSync').mockImplementation(() => undefined) + + const plugin = useClassy({ + debug: true, + manifestRoot: '/project', + }) as Plugin + + if (plugin.configResolved) { + await plugin.configResolved({ + command: 'build', + root: '/project', + build: { ssr: false }, + consumer: 'server', + } as never) + } + + const transform = plugin.transform as ( + this: { addWatchFile: (id: string) => void }, + code: string, + id: string, + ) => { code: string } | null + + transform.call( + { addWatchFile: vi.fn() }, + '
x
', + '/project/Component.vue', + ) + + writeSpy.mockClear() + + if (typeof plugin.renderStart === 'function') { + await plugin.renderStart.call({ + environment: { name: 'ssr', config: { consumer: 'server' } }, + } as never) + } + + if (typeof plugin.generateBundle === 'function') { + const generateBundle = plugin.generateBundle as ( + this: unknown, + outputOptions: object, + bundle: object, + isWrite?: boolean, + ) => void | Promise + await generateBundle.call({}, {}, {}, false) + } + + if (typeof plugin.buildEnd === 'function') { + await plugin.buildEnd.call({} as never) + } + + expect( + writeSpy.mock.calls.some(([filePath]) => + String(filePath).includes('output.classy.html'), + ), + ).toBe(false) + + writeSpy.mockRestore() + }) + it('should flush the manifest on generateBundle during builds', async () => { const fs = await import('fs') const writeSpy = vi.spyOn(fs.default, 'writeFileSync').mockImplementation(() => undefined)