From c4c0285751052b2ddbafaf790e47459bf0c7e4c6 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 3 Jul 2025 11:38:57 +0100 Subject: [PATCH 1/5] Add comprehensive test suite with Vitest - Set up Vitest testing framework with jsdom environment - Add @vue/test-utils for Vue component testing - Create comprehensive tests for debugMixin functionality - Test plugin installation and global mixin behavior - Add test scripts to package.json - Mock console methods to verify debug output - Test all log levels and configuration options - Verify lifecycle event logging --- package.json | 9 +- tests/debugMixin.test.js | 229 +++++++++++++++++++++++++++++++++++++++ tests/plugin.test.js | 165 ++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+), 3 deletions(-) create mode 100644 tests/debugMixin.test.js create mode 100644 tests/plugin.test.js diff --git a/package.json b/package.json index 11b420e..21d0e3a 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", - "prepublishOnly": "npm run build", - "prettier:format": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue}\"", - "prettier:check": "prettier --check \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue}\"" + "test": "vitest", + "test:run": "vitest run", + "prepublishOnly": "npm run build" }, "keywords": [ "vue", @@ -35,6 +35,9 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.0", + "@vue/test-utils": "^2.4.0", + "jsdom": "^23.0.0", + "vitest": "^1.0.0", "vite": "^5.0.0", "vue": "^3.4.0" }, diff --git a/tests/debugMixin.test.js b/tests/debugMixin.test.js new file mode 100644 index 0000000..e78c1a7 --- /dev/null +++ b/tests/debugMixin.test.js @@ -0,0 +1,229 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { mount } from '@vue/test-utils' +import { createDebugMixin } from '../src/mixins/debugMixin.js' + +// Mock console methods +const mockConsole = { + log: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} + +describe('createDebugMixin', () => { + beforeEach(() => { + // Mock console methods + vi.spyOn(console, 'log').mockImplementation(mockConsole.log) + vi.spyOn(console, 'info').mockImplementation(mockConsole.info) + vi.spyOn(console, 'warn').mockImplementation(mockConsole.warn) + vi.spyOn(console, 'error').mockImplementation(mockConsole.error) + }) + + afterEach(() => { + vi.restoreAllMocks() + mockConsole.log.mockClear() + mockConsole.info.mockClear() + mockConsole.warn.mockClear() + mockConsole.error.mockClear() + }) + + it('creates a mixin with default options', () => { + const mixin = createDebugMixin() + + expect(mixin).toHaveProperty('data') + expect(mixin).toHaveProperty('methods') + expect(mixin).toHaveProperty('created') + expect(mixin).toHaveProperty('mounted') + expect(mixin).toHaveProperty('beforeUnmount') + }) + + it('creates a mixin with custom options', () => { + const options = { + enabled: true, + logLevel: 'info', + prefix: '[Custom Debug]' + } + + const mixin = createDebugMixin(options) + const data = mixin.data() + + expect(data.$debugEnabled).toBe(true) + expect(data.$debugPrefix).toBe('[Custom Debug]') + }) + + it('provides debug methods to component', () => { + const TestComponent = { + name: 'TestComponent', + mixins: [createDebugMixin({ enabled: true })], + template: '
Test
' + } + + const wrapper = mount(TestComponent) + const vm = wrapper.vm + + expect(vm.$debug).toBeDefined() + expect(vm.$debugInfo).toBeDefined() + expect(vm.$debugWarn).toBeDefined() + expect(vm.$debugError).toBeDefined() + }) + + it('logs debug messages when enabled', () => { + const TestComponent = { + name: 'TestComponent', + mixins: [createDebugMixin({ enabled: true, logLevel: 'debug' })], + template: '
Test
', + mounted() { + this.$debug('Test debug message', { data: 'test' }) + } + } + + mount(TestComponent) + + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + 'Test debug message', + { data: 'test' } + ) + }) + + it('does not log when disabled', () => { + const TestComponent = { + name: 'TestComponent', + mixins: [createDebugMixin({ enabled: false })], + template: '
Test
', + mounted() { + this.$debug('This should not log') + } + } + + mount(TestComponent) + + expect(mockConsole.log).not.toHaveBeenCalled() + }) + + it('respects log level filtering', () => { + const TestComponent = { + name: 'TestComponent', + mixins: [createDebugMixin({ enabled: true, logLevel: 'warn' })], + template: '
Test
', + mounted() { + this.$debug('Debug message') // Should not log + this.$debugInfo('Info message') // Should not log + this.$debugWarn('Warn message') // Should log + this.$debugError('Error message') // Should log + } + } + + mount(TestComponent) + + expect(mockConsole.log).not.toHaveBeenCalled() + expect(mockConsole.info).not.toHaveBeenCalled() + expect(mockConsole.warn).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + 'Warn message' + ) + expect(mockConsole.error).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + 'Error message' + ) + }) + + it('uses custom prefix in log messages', () => { + const TestComponent = { + name: 'TestComponent', + mixins: [createDebugMixin({ + enabled: true, + prefix: '[Custom Prefix]' + })], + template: '
Test
', + mounted() { + this.$debug('Test message') + } + } + + mount(TestComponent) + + expect(mockConsole.log).toHaveBeenCalledWith( + '[Custom Prefix] [TestComponent]', + 'Test message' + ) + }) + + it('handles components without names', () => { + const TestComponent = { + // No name property + mixins: [createDebugMixin({ enabled: true })], + template: '
Test
', + mounted() { + this.$debug('Test message') + } + } + + mount(TestComponent) + + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [Component]', + 'Test message' + ) + }) + + it('logs lifecycle events automatically', () => { + const TestComponent = { + name: 'TestComponent', + mixins: [createDebugMixin({ enabled: true })], + template: '
Test
' + } + + const wrapper = mount(TestComponent) + + // Check created lifecycle log + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + 'Component created', + expect.objectContaining({ + name: 'TestComponent' + }) + ) + + // Check mounted lifecycle log + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + 'Component mounted', + expect.objectContaining({ + element: expect.any(Object) + }) + ) + + // Test beforeUnmount + wrapper.unmount() + + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + 'Component before unmount' + ) + }) + + it('works with different log levels', () => { + const levels = ['debug', 'info', 'warn', 'error'] + + levels.forEach(level => { + const TestComponent = { + name: 'TestComponent', + mixins: [createDebugMixin({ enabled: true, logLevel: level })], + template: '
Test
', + mounted() { + this[`$${level}`](`${level} message`) + } + } + + mount(TestComponent) + + expect(mockConsole[level]).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + `${level} message` + ) + + mockConsole[level].mockClear() + }) + }) +}) \ No newline at end of file diff --git a/tests/plugin.test.js b/tests/plugin.test.js new file mode 100644 index 0000000..b9885b9 --- /dev/null +++ b/tests/plugin.test.js @@ -0,0 +1,165 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { createApp } from 'vue' +import { mount } from '@vue/test-utils' +import VueComponentDebug from '../src/index.js' + +// Mock console methods +const mockConsole = { + log: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn() +} + +describe('VueComponentDebug Plugin', () => { + beforeEach(() => { + // Mock console methods + vi.spyOn(console, 'log').mockImplementation(mockConsole.log) + vi.spyOn(console, 'info').mockImplementation(mockConsole.info) + vi.spyOn(console, 'warn').mockImplementation(mockConsole.warn) + vi.spyOn(console, 'error').mockImplementation(mockConsole.error) + }) + + afterEach(() => { + vi.restoreAllMocks() + mockConsole.log.mockClear() + mockConsole.info.mockClear() + mockConsole.warn.mockClear() + mockConsole.error.mockClear() + }) + + it('installs plugin with default options', () => { + const app = createApp({}) + const mockMixin = vi.fn() + app.mixin = mockMixin + + VueComponentDebug.install(app) + + expect(mockMixin).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.any(Function), + methods: expect.any(Object), + created: expect.any(Function), + mounted: expect.any(Function), + beforeUnmount: expect.any(Function) + }) + ) + }) + + it('installs plugin with custom options', () => { + const app = createApp({}) + const mockMixin = vi.fn() + app.mixin = mockMixin + + const options = { + enabled: true, + logLevel: 'info', + prefix: '[Custom]' + } + + VueComponentDebug.install(app, options) + + expect(mockMixin).toHaveBeenCalled() + + // Test that the mixin was created with the right options + const mixinArg = mockMixin.mock.calls[0][0] + const data = mixinArg.data() + expect(data.$debugEnabled).toBe(true) + expect(data.$debugPrefix).toBe('[Custom]') + }) + + it('adds debug methods to all components when installed globally', () => { + const TestComponent = { + name: 'TestComponent', + template: '
Test
', + mounted() { + this.$debug('Global mixin test') + } + } + + // Mount with global plugin + const wrapper = mount(TestComponent, { + global: { + plugins: [[VueComponentDebug, { enabled: true }]] + } + }) + + const vm = wrapper.vm + + expect(vm.$debug).toBeDefined() + expect(vm.$debugInfo).toBeDefined() + expect(vm.$debugWarn).toBeDefined() + expect(vm.$debugError).toBeDefined() + + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [TestComponent]', + 'Global mixin test' + ) + }) + + it('works with multiple components', () => { + const Component1 = { + name: 'Component1', + template: '
Component 1
', + mounted() { + this.$debug('Component 1 mounted') + } + } + + const Component2 = { + name: 'Component2', + template: '
Component 2
', + mounted() { + this.$debug('Component 2 mounted') + } + } + + mount(Component1, { + global: { + plugins: [[VueComponentDebug, { enabled: true }]] + } + }) + + mount(Component2, { + global: { + plugins: [[VueComponentDebug, { enabled: true }]] + } + }) + + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [Component1]', + 'Component 1 mounted' + ) + + expect(mockConsole.log).toHaveBeenCalledWith( + '[Vue Debug] [Component2]', + 'Component 2 mounted' + ) + }) + + it('respects environment-based default enabling', () => { + const originalEnv = process.env.NODE_ENV + + // Test development mode (should be enabled by default) + process.env.NODE_ENV = 'development' + + const TestComponent = { + name: 'TestComponent', + template: '
Test
', + mounted() { + this.$debug('Development mode test') + } + } + + mount(TestComponent, { + global: { + plugins: [VueComponentDebug] // No explicit options + } + }) + + expect(mockConsole.log).toHaveBeenCalled() + + // Restore original environment + process.env.NODE_ENV = originalEnv + }) +}) \ No newline at end of file From 1804807100405cb879c114ff807a315d689182a8 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 3 Jul 2025 11:52:10 +0100 Subject: [PATCH 2/5] Fix tests to work with jsdom environment - Configure Vite properly for jsdom test environment - Update tests to match actual component behavior in test environment - Fix component naming expectations (falls back to 'Anonymous' in tests) - Test production environment behavior (no comments added) - Test HTML comment insertion and cleanup functionality - All 16 tests now passing --- package-lock.json | 2523 +++++++++++++++++++++++++++++++++++--- package.json | 4 +- tests/debugMixin.test.js | 333 ++--- tests/plugin.test.js | 208 ++-- vite.config.js | 42 +- 5 files changed, 2696 insertions(+), 414 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d50fbc..dbc3fd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,42 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.0", + "@vue/test-utils": "^2.4.0", + "jsdom": "^23.0.0", "vite": "^5.0.0", + "vitest": "^1.0.0", "vue": "^3.4.0" }, "peerDependencies": { "vue": "^3.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz", + "integrity": "sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.3", + "css-tree": "^2.3.1", + "is-potential-custom-element-name": "^1.0.1" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -70,6 +99,121 @@ "node": ">=6.9.0" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -461,6 +605,37 @@ "node": ">=12" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", @@ -468,6 +643,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.44.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", @@ -748,6 +941,13 @@ "win32" ] }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -769,6 +969,90 @@ "vue": "^3.2.25" } }, + "node_modules/@vitest/expect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/@vue/compiler-core": { "version": "3.5.17", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz", @@ -878,125 +1162,1334 @@ "dev": true, "license": "MIT" }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "node_modules/@vue/test-utils": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "ISC", "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, - "hasInstallScript": true, "license": "MIT", "bin": { - "esbuild": "bin/esbuild" + "acorn": "bin/acorn" }, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "node": ">=0.4.0" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 14" } }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "*" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/postcss": { - "version": "8.5.6", + "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/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.2.0.tgz", + "integrity": "sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/dom-selector": "^2.0.1", + "cssstyle": "^4.0.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.16.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/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/postcss": { + "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, @@ -1021,72 +2514,448 @@ "source-map-js": "^1.2.1" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", + "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.44.1", + "@rollup/rollup-android-arm64": "4.44.1", + "@rollup/rollup-darwin-arm64": "4.44.1", + "@rollup/rollup-darwin-x64": "4.44.1", + "@rollup/rollup-freebsd-arm64": "4.44.1", + "@rollup/rollup-freebsd-x64": "4.44.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", + "@rollup/rollup-linux-arm-musleabihf": "4.44.1", + "@rollup/rollup-linux-arm64-gnu": "4.44.1", + "@rollup/rollup-linux-arm64-musl": "4.44.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", + "@rollup/rollup-linux-riscv64-gnu": "4.44.1", + "@rollup/rollup-linux-riscv64-musl": "4.44.1", + "@rollup/rollup-linux-s390x-gnu": "4.44.1", + "@rollup/rollup-linux-x64-gnu": "4.44.1", + "@rollup/rollup-linux-x64-musl": "4.44.1", + "@rollup/rollup-win32-arm64-msvc": "4.44.1", + "@rollup/rollup-win32-ia32-msvc": "4.44.1", + "@rollup/rollup-win32-x64-msvc": "4.44.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" } }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, "engines": { - "node": ">=14" + "node": ">=14.0.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "engines": { + "node": ">=6" } }, - "node_modules/rollup": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", - "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" + "punycode": "^2.3.1" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.1", - "@rollup/rollup-android-arm64": "4.44.1", - "@rollup/rollup-darwin-arm64": "4.44.1", - "@rollup/rollup-darwin-x64": "4.44.1", - "@rollup/rollup-freebsd-arm64": "4.44.1", - "@rollup/rollup-freebsd-x64": "4.44.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", - "@rollup/rollup-linux-arm-musleabihf": "4.44.1", - "@rollup/rollup-linux-arm64-gnu": "4.44.1", - "@rollup/rollup-linux-arm64-musl": "4.44.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-musl": "4.44.1", - "@rollup/rollup-linux-s390x-gnu": "4.44.1", - "@rollup/rollup-linux-x64-gnu": "4.44.1", - "@rollup/rollup-linux-x64-musl": "4.44.1", - "@rollup/rollup-win32-arm64-msvc": "4.44.1", - "@rollup/rollup-win32-ia32-msvc": "4.44.1", - "@rollup/rollup-win32-x64-msvc": "4.44.1", - "fsevents": "~2.3.2" + "node": ">=18" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/typescript": { @@ -1105,6 +2974,34 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/vite": { "version": "5.4.19", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", @@ -1165,6 +3062,95 @@ } } }, + "node_modules/vite-node": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", + "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/vue": { "version": "3.5.17", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz", @@ -1186,6 +3172,269 @@ "optional": true } } + }, + "node_modules/vue-component-type-helpers": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 21d0e3a..27ed004 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ "preview": "vite preview", "test": "vitest", "test:run": "vitest run", - "prepublishOnly": "npm run build" + "prepublishOnly": "npm run build", + "prettier:format": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue}\"", + "prettier:check": "prettier --check \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue}\"" }, "keywords": [ "vue", diff --git a/tests/debugMixin.test.js b/tests/debugMixin.test.js index e78c1a7..4ebc8c3 100644 --- a/tests/debugMixin.test.js +++ b/tests/debugMixin.test.js @@ -1,229 +1,232 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { mount } from '@vue/test-utils' -import { createDebugMixin } from '../src/mixins/debugMixin.js' +import { createComponentDebugMixin } from '../src/mixins/componentDebug.js' -// Mock console methods -const mockConsole = { - log: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn() -} +describe('createComponentDebugMixin', () => { + let originalEnv -describe('createDebugMixin', () => { beforeEach(() => { - // Mock console methods - vi.spyOn(console, 'log').mockImplementation(mockConsole.log) - vi.spyOn(console, 'info').mockImplementation(mockConsole.info) - vi.spyOn(console, 'warn').mockImplementation(mockConsole.warn) - vi.spyOn(console, 'error').mockImplementation(mockConsole.error) + originalEnv = process.env.NODE_ENV }) afterEach(() => { - vi.restoreAllMocks() - mockConsole.log.mockClear() - mockConsole.info.mockClear() - mockConsole.warn.mockClear() - mockConsole.error.mockClear() + process.env.NODE_ENV = originalEnv }) - it('creates a mixin with default options', () => { - const mixin = createDebugMixin() + it('creates a mixin with mounted and beforeUnmount hooks', () => { + const mixin = createComponentDebugMixin() - expect(mixin).toHaveProperty('data') - expect(mixin).toHaveProperty('methods') - expect(mixin).toHaveProperty('created') expect(mixin).toHaveProperty('mounted') expect(mixin).toHaveProperty('beforeUnmount') + expect(typeof mixin.mounted).toBe('function') + expect(typeof mixin.beforeUnmount).toBe('function') }) - it('creates a mixin with custom options', () => { - const options = { - enabled: true, - logLevel: 'info', - prefix: '[Custom Debug]' - } - - const mixin = createDebugMixin(options) - const data = mixin.data() + it('adds HTML comments around component in development mode', () => { + process.env.NODE_ENV = 'development' - expect(data.$debugEnabled).toBe(true) - expect(data.$debugPrefix).toBe('[Custom Debug]') - }) - - it('provides debug methods to component', () => { const TestComponent = { name: 'TestComponent', - mixins: [createDebugMixin({ enabled: true })], - template: '
Test
' + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
' } - const wrapper = mount(TestComponent) - const vm = wrapper.vm + const wrapper = mount(TestComponent, { + attachTo: document.body + }) + + const element = wrapper.element + const parent = element.parentNode - expect(vm.$debug).toBeDefined() - expect(vm.$debugInfo).toBeDefined() - expect(vm.$debugWarn).toBeDefined() - expect(vm.$debugError).toBeDefined() + // Check for start comment + const prevSibling = element.previousSibling + expect(prevSibling).toBeTruthy() + expect(prevSibling.nodeType).toBe(Node.COMMENT_NODE) + expect(prevSibling.nodeValue).toBe(' Start component: src/components/TestComponent.vue ') + + // Check for end comment + const nextSibling = element.nextSibling + expect(nextSibling).toBeTruthy() + expect(nextSibling.nodeType).toBe(Node.COMMENT_NODE) + expect(nextSibling.nodeValue).toBe(' End component: src/components/TestComponent.vue ') + + wrapper.unmount() }) - it('logs debug messages when enabled', () => { + it('uses component name when __file is not available', () => { + process.env.NODE_ENV = 'development' + const TestComponent = { - name: 'TestComponent', - mixins: [createDebugMixin({ enabled: true, logLevel: 'debug' })], - template: '
Test
', - mounted() { - this.$debug('Test debug message', { data: 'test' }) - } + __name: 'MyComponent', + mixins: [createComponentDebugMixin()], + template: '
Test Content
' } - mount(TestComponent) + const wrapper = mount(TestComponent, { + attachTo: document.body + }) + + const element = wrapper.element + const prevSibling = element.previousSibling - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - 'Test debug message', - { data: 'test' } - ) + expect(prevSibling.nodeValue).toBe(' Start component: MyComponent ') + + wrapper.unmount() }) - it('does not log when disabled', () => { + it('uses "Anonymous" when no component identifier is available', () => { + process.env.NODE_ENV = 'development' + const TestComponent = { - name: 'TestComponent', - mixins: [createDebugMixin({ enabled: false })], - template: '
Test
', - mounted() { - this.$debug('This should not log') - } + mixins: [createComponentDebugMixin()], + template: '
Test Content
' } - mount(TestComponent) + const wrapper = mount(TestComponent, { + attachTo: document.body + }) + + const element = wrapper.element + const prevSibling = element.previousSibling + + expect(prevSibling.nodeValue).toBe(' Start component: Anonymous ') - expect(mockConsole.log).not.toHaveBeenCalled() + wrapper.unmount() }) - it('respects log level filtering', () => { + it('does not add comments in production mode', () => { + process.env.NODE_ENV = 'production' + const TestComponent = { name: 'TestComponent', - mixins: [createDebugMixin({ enabled: true, logLevel: 'warn' })], - template: '
Test
', - mounted() { - this.$debug('Debug message') // Should not log - this.$debugInfo('Info message') // Should not log - this.$debugWarn('Warn message') // Should log - this.$debugError('Error message') // Should log - } + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
' } - mount(TestComponent) - - expect(mockConsole.log).not.toHaveBeenCalled() - expect(mockConsole.info).not.toHaveBeenCalled() - expect(mockConsole.warn).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - 'Warn message' - ) - expect(mockConsole.error).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - 'Error message' - ) + const wrapper = mount(TestComponent, { + attachTo: document.body + }) + + const element = wrapper.element + + // Should not have comment siblings in production + expect(element.previousSibling).toBeNull() + expect(element.nextSibling).toBeNull() + + wrapper.unmount() }) - it('uses custom prefix in log messages', () => { + it('cleans up comments when component is unmounted', () => { + process.env.NODE_ENV = 'development' + const TestComponent = { name: 'TestComponent', - mixins: [createDebugMixin({ - enabled: true, - prefix: '[Custom Prefix]' - })], - template: '
Test
', - mounted() { - this.$debug('Test message') - } + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
' } - mount(TestComponent) + const wrapper = mount(TestComponent, { + attachTo: document.body + }) + + const element = wrapper.element + const parent = element.parentNode + + // Verify comments exist + expect(element.previousSibling?.nodeType).toBe(Node.COMMENT_NODE) + expect(element.nextSibling?.nodeType).toBe(Node.COMMENT_NODE) + + // Store references to comments + const startComment = element.previousSibling + const endComment = element.nextSibling + + // Unmount component + wrapper.unmount() - expect(mockConsole.log).toHaveBeenCalledWith( - '[Custom Prefix] [TestComponent]', - 'Test message' - ) + // Verify comments are removed from DOM + expect(parent.contains(startComment)).toBe(false) + expect(parent.contains(endComment)).toBe(false) }) - it('handles components without names', () => { - const TestComponent = { - // No name property - mixins: [createDebugMixin({ enabled: true })], - template: '
Test
', - mounted() { - this.$debug('Test message') - } + it('handles multiple components with different names', () => { + process.env.NODE_ENV = 'development' + + const Component1 = { + __file: 'src/components/Component1.vue', + mixins: [createComponentDebugMixin()], + template: '
Component 1
' } - mount(TestComponent) + const Component2 = { + __file: 'src/components/Component2.vue', + mixins: [createComponentDebugMixin()], + template: '
Component 2
' + } - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [Component]', - 'Test message' - ) + const wrapper1 = mount(Component1, { attachTo: document.body }) + const wrapper2 = mount(Component2, { attachTo: document.body }) + + expect(wrapper1.element.previousSibling.nodeValue).toBe(' Start component: src/components/Component1.vue ') + expect(wrapper2.element.previousSibling.nodeValue).toBe(' Start component: src/components/Component2.vue ') + + wrapper1.unmount() + wrapper2.unmount() }) - it('logs lifecycle events automatically', () => { + it('handles components without parent nodes gracefully', () => { + process.env.NODE_ENV = 'development' + const TestComponent = { - name: 'TestComponent', - mixins: [createDebugMixin({ enabled: true })], - template: '
Test
' + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
' } + // Mount without attaching to document const wrapper = mount(TestComponent) - // Check created lifecycle log - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - 'Component created', - expect.objectContaining({ - name: 'TestComponent' - }) - ) - - // Check mounted lifecycle log - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - 'Component mounted', - expect.objectContaining({ - element: expect.any(Object) - }) - ) - - // Test beforeUnmount - wrapper.unmount() - - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - 'Component before unmount' - ) + // Should not throw errors when parent is null + expect(() => { + wrapper.unmount() + }).not.toThrow() }) - it('works with different log levels', () => { - const levels = ['debug', 'info', 'warn', 'error'] + it('only removes matching comments during cleanup', () => { + process.env.NODE_ENV = 'development' - levels.forEach(level => { - const TestComponent = { - name: 'TestComponent', - mixins: [createDebugMixin({ enabled: true, logLevel: level })], - template: '
Test
', - mounted() { - this[`$${level}`](`${level} message`) - } - } - - mount(TestComponent) - - expect(mockConsole[level]).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - `${level} message` - ) - - mockConsole[level].mockClear() + const TestComponent = { + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
' + } + + const container = document.createElement('div') + document.body.appendChild(container) + + // Add some unrelated comments + const unrelatedComment1 = document.createComment(' Some other comment ') + const unrelatedComment2 = document.createComment(' Another comment ') + container.appendChild(unrelatedComment1) + container.appendChild(unrelatedComment2) + + const wrapper = mount(TestComponent, { + attachTo: container }) + + // Verify our component comments exist + const element = wrapper.element + expect(element.previousSibling.nodeValue).toBe(' Start component: src/components/TestComponent.vue ') + expect(element.nextSibling.nodeValue).toBe(' End component: src/components/TestComponent.vue ') + + wrapper.unmount() + + // Unrelated comments should still exist + expect(container.contains(unrelatedComment1)).toBe(true) + expect(container.contains(unrelatedComment2)).toBe(true) + + document.body.removeChild(container) }) }) \ No newline at end of file diff --git a/tests/plugin.test.js b/tests/plugin.test.js index b9885b9..88bbedf 100644 --- a/tests/plugin.test.js +++ b/tests/plugin.test.js @@ -3,32 +3,18 @@ import { createApp } from 'vue' import { mount } from '@vue/test-utils' import VueComponentDebug from '../src/index.js' -// Mock console methods -const mockConsole = { - log: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn() -} - describe('VueComponentDebug Plugin', () => { + let originalEnv + beforeEach(() => { - // Mock console methods - vi.spyOn(console, 'log').mockImplementation(mockConsole.log) - vi.spyOn(console, 'info').mockImplementation(mockConsole.info) - vi.spyOn(console, 'warn').mockImplementation(mockConsole.warn) - vi.spyOn(console, 'error').mockImplementation(mockConsole.error) + originalEnv = process.env.NODE_ENV }) afterEach(() => { - vi.restoreAllMocks() - mockConsole.log.mockClear() - mockConsole.info.mockClear() - mockConsole.warn.mockClear() - mockConsole.error.mockClear() + process.env.NODE_ENV = originalEnv }) - it('installs plugin with default options', () => { + it('installs plugin and adds mixin to app', () => { const app = createApp({}) const mockMixin = vi.fn() app.mixin = mockMixin @@ -37,129 +23,167 @@ describe('VueComponentDebug Plugin', () => { expect(mockMixin).toHaveBeenCalledWith( expect.objectContaining({ - data: expect.any(Function), - methods: expect.any(Object), - created: expect.any(Function), mounted: expect.any(Function), beforeUnmount: expect.any(Function) }) ) }) - it('installs plugin with custom options', () => { - const app = createApp({}) - const mockMixin = vi.fn() - app.mixin = mockMixin - - const options = { - enabled: true, - logLevel: 'info', - prefix: '[Custom]' - } - - VueComponentDebug.install(app, options) - - expect(mockMixin).toHaveBeenCalled() + it('adds debug comments to all components when installed globally in development', () => { + process.env.NODE_ENV = 'development' - // Test that the mixin was created with the right options - const mixinArg = mockMixin.mock.calls[0][0] - const data = mixinArg.data() - expect(data.$debugEnabled).toBe(true) - expect(data.$debugPrefix).toBe('[Custom]') - }) - - it('adds debug methods to all components when installed globally', () => { const TestComponent = { name: 'TestComponent', - template: '
Test
', - mounted() { - this.$debug('Global mixin test') - } + template: '
Test
' } // Mount with global plugin const wrapper = mount(TestComponent, { + attachTo: document.body, global: { - plugins: [[VueComponentDebug, { enabled: true }]] + plugins: [VueComponentDebug] } }) - const vm = wrapper.vm + const element = wrapper.element - expect(vm.$debug).toBeDefined() - expect(vm.$debugInfo).toBeDefined() - expect(vm.$debugWarn).toBeDefined() - expect(vm.$debugError).toBeDefined() + // Check for debug comments (will use "Anonymous" since __file is not available in test) + expect(element.previousSibling?.nodeType).toBe(Node.COMMENT_NODE) + expect(element.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') + expect(element.nextSibling?.nodeType).toBe(Node.COMMENT_NODE) + expect(element.nextSibling?.nodeValue).toBe(' End component: Anonymous ') - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [TestComponent]', - 'Global mixin test' - ) + wrapper.unmount() }) it('works with multiple components', () => { + process.env.NODE_ENV = 'development' + const Component1 = { - name: 'Component1', - template: '
Component 1
', - mounted() { - this.$debug('Component 1 mounted') - } + template: '
Component 1
' } const Component2 = { - name: 'Component2', - template: '
Component 2
', - mounted() { - this.$debug('Component 2 mounted') - } + template: '
Component 2
' } - mount(Component1, { + const wrapper1 = mount(Component1, { + attachTo: document.body, global: { - plugins: [[VueComponentDebug, { enabled: true }]] + plugins: [VueComponentDebug] } }) - mount(Component2, { + const wrapper2 = mount(Component2, { + attachTo: document.body, global: { - plugins: [[VueComponentDebug, { enabled: true }]] + plugins: [VueComponentDebug] } }) - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [Component1]', - 'Component 1 mounted' - ) + // Both should use "Anonymous" since no component identifier is available in test environment + expect(wrapper1.element.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') + expect(wrapper2.element.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') - expect(mockConsole.log).toHaveBeenCalledWith( - '[Vue Debug] [Component2]', - 'Component 2 mounted' - ) + wrapper1.unmount() + wrapper2.unmount() }) - it('respects environment-based default enabling', () => { - const originalEnv = process.env.NODE_ENV + it('does not add comments in production mode', () => { + process.env.NODE_ENV = 'production' - // Test development mode (should be enabled by default) + const TestComponent = { + name: 'TestComponent', + __file: 'src/components/TestComponent.vue', + template: '
Test
' + } + + const wrapper = mount(TestComponent, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug] + } + }) + + const element = wrapper.element + + // Should not have comment siblings in production + expect(element.previousSibling).toBeNull() + expect(element.nextSibling).toBeNull() + + wrapper.unmount() + }) + + it('handles app parameter gracefully when undefined', () => { + // Test that the plugin throws when app doesn't have mixin method + expect(() => { + VueComponentDebug.install() + }).toThrow('app.mixin is not a function') + }) + + it('cleans up comments when components are unmounted globally', () => { process.env.NODE_ENV = 'development' const TestComponent = { - name: 'TestComponent', - template: '
Test
', - mounted() { - this.$debug('Development mode test') + __file: 'src/components/TestComponent.vue', + template: '
Test Content
' + } + + const wrapper = mount(TestComponent, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug] } + }) + + const element = wrapper.element + const parent = element.parentNode + + // Store references to comments + const startComment = element.previousSibling + const endComment = element.nextSibling + + // Verify comments exist + expect(startComment?.nodeType).toBe(Node.COMMENT_NODE) + expect(endComment?.nodeType).toBe(Node.COMMENT_NODE) + + // Unmount component + wrapper.unmount() + + // Verify comments are removed from DOM + expect(parent.contains(startComment)).toBe(false) + expect(parent.contains(endComment)).toBe(false) + }) + + it('works with nested components', () => { + process.env.NODE_ENV = 'development' + + const ChildComponent = { + template: 'Child' + } + + const ParentComponent = { + components: { ChildComponent }, + template: '
Parent
' } - mount(TestComponent, { + const wrapper = mount(ParentComponent, { + attachTo: document.body, global: { - plugins: [VueComponentDebug] // No explicit options + plugins: [VueComponentDebug] } }) - expect(mockConsole.log).toHaveBeenCalled() + // Check parent component comments (will use "Anonymous" in test environment) + const parentElement = wrapper.element + expect(parentElement.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') + expect(parentElement.nextSibling?.nodeValue).toBe(' End component: Anonymous ') - // Restore original environment - process.env.NODE_ENV = originalEnv + // Check child component comments + const childElement = wrapper.findComponent(ChildComponent).element + expect(childElement.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') + expect(childElement.nextSibling?.nodeValue).toBe(' End component: Anonymous ') + + wrapper.unmount() }) }) \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index e45ff0b..7cecd86 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,23 +1,27 @@ -import { defineConfig } from 'vite'; -import vue from '@vitejs/plugin-vue'; -import { resolve } from 'path'; +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { resolve } from 'path' export default defineConfig({ - plugins: [vue()], - build: { - lib: { - entry: resolve(__dirname, 'src/index.js'), - name: 'VueComponentDebug', - formats: ['es', 'umd'], - fileName: (format) => `vue-component-debug.${format === 'es' ? 'js' : 'umd.cjs'}`, - }, - rollupOptions: { - external: ['vue'], - output: { - globals: { - vue: 'Vue', - }, - }, + plugins: [vue()], + build: { + lib: { + entry: resolve(__dirname, 'src/index.js'), + name: 'VueComponentDebug', + formats: ['es', 'umd'], + fileName: (format) => `vue-component-debug.${format === 'es' ? 'js' : 'umd.cjs'}`, + }, + rollupOptions: { + external: ['vue'], + output: { + globals: { + vue: 'Vue', }, + }, }, -}); + }, + test: { + environment: 'jsdom', + globals: true + }, +}) From c07d0e7c2a5be4c101103d85d7b3f2ab26eafc87 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 3 Jul 2025 11:55:38 +0100 Subject: [PATCH 3/5] Add GitHub Actions CI workflow - Run tests on push to main and add-tests branches - Test on Node.js 18.x, 20.x, and 22.x - Run tests, build, and prettier formatting checks - Use npm ci for faster, reliable installs - Trigger on pull requests to main branch --- .github/workflows/ci.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d8c95f7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI + +on: + push: + branches: [ main, add-tests ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm run test:run + + - name: Run build + run: npm run build + + - name: Check formatting + run: npm run prettier:check \ No newline at end of file From 66ab5555d7c4c049eb39aa949e8f6196d1962e06 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 3 Jul 2025 11:57:05 +0100 Subject: [PATCH 4/5] Rename actions workflow --- .github/workflows/{ci.yml => tests.yml} | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename .github/workflows/{ci.yml => tests.yml} (90%) diff --git a/.github/workflows/ci.yml b/.github/workflows/tests.yml similarity index 90% rename from .github/workflows/ci.yml rename to .github/workflows/tests.yml index d8c95f7..5ccff10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/tests.yml @@ -1,10 +1,8 @@ -name: CI +name: Tests on: push: - branches: [ main, add-tests ] pull_request: - branches: [ main ] jobs: test: From 8635900e6895d8ae9c0b13061c502188c62296ac Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 3 Jul 2025 11:58:12 +0100 Subject: [PATCH 5/5] Run Prettier --- tests/debugMixin.test.js | 460 +++++++++++++++++++-------------------- tests/plugin.test.js | 374 +++++++++++++++---------------- vite.config.js | 46 ++-- 3 files changed, 440 insertions(+), 440 deletions(-) diff --git a/tests/debugMixin.test.js b/tests/debugMixin.test.js index 4ebc8c3..290db8c 100644 --- a/tests/debugMixin.test.js +++ b/tests/debugMixin.test.js @@ -1,232 +1,232 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' -import { mount } from '@vue/test-utils' -import { createComponentDebugMixin } from '../src/mixins/componentDebug.js' +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { mount } from '@vue/test-utils'; +import { createComponentDebugMixin } from '../src/mixins/componentDebug.js'; describe('createComponentDebugMixin', () => { - let originalEnv - - beforeEach(() => { - originalEnv = process.env.NODE_ENV - }) - - afterEach(() => { - process.env.NODE_ENV = originalEnv - }) - - it('creates a mixin with mounted and beforeUnmount hooks', () => { - const mixin = createComponentDebugMixin() - - expect(mixin).toHaveProperty('mounted') - expect(mixin).toHaveProperty('beforeUnmount') - expect(typeof mixin.mounted).toBe('function') - expect(typeof mixin.beforeUnmount).toBe('function') - }) - - it('adds HTML comments around component in development mode', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - name: 'TestComponent', - __file: 'src/components/TestComponent.vue', - mixins: [createComponentDebugMixin()], - template: '
Test Content
' - } - - const wrapper = mount(TestComponent, { - attachTo: document.body - }) - - const element = wrapper.element - const parent = element.parentNode - - // Check for start comment - const prevSibling = element.previousSibling - expect(prevSibling).toBeTruthy() - expect(prevSibling.nodeType).toBe(Node.COMMENT_NODE) - expect(prevSibling.nodeValue).toBe(' Start component: src/components/TestComponent.vue ') - - // Check for end comment - const nextSibling = element.nextSibling - expect(nextSibling).toBeTruthy() - expect(nextSibling.nodeType).toBe(Node.COMMENT_NODE) - expect(nextSibling.nodeValue).toBe(' End component: src/components/TestComponent.vue ') - - wrapper.unmount() - }) - - it('uses component name when __file is not available', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - __name: 'MyComponent', - mixins: [createComponentDebugMixin()], - template: '
Test Content
' - } - - const wrapper = mount(TestComponent, { - attachTo: document.body - }) - - const element = wrapper.element - const prevSibling = element.previousSibling - - expect(prevSibling.nodeValue).toBe(' Start component: MyComponent ') - - wrapper.unmount() - }) - - it('uses "Anonymous" when no component identifier is available', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - mixins: [createComponentDebugMixin()], - template: '
Test Content
' - } - - const wrapper = mount(TestComponent, { - attachTo: document.body - }) - - const element = wrapper.element - const prevSibling = element.previousSibling - - expect(prevSibling.nodeValue).toBe(' Start component: Anonymous ') - - wrapper.unmount() - }) - - it('does not add comments in production mode', () => { - process.env.NODE_ENV = 'production' - - const TestComponent = { - name: 'TestComponent', - __file: 'src/components/TestComponent.vue', - mixins: [createComponentDebugMixin()], - template: '
Test Content
' - } - - const wrapper = mount(TestComponent, { - attachTo: document.body - }) - - const element = wrapper.element - - // Should not have comment siblings in production - expect(element.previousSibling).toBeNull() - expect(element.nextSibling).toBeNull() - - wrapper.unmount() - }) - - it('cleans up comments when component is unmounted', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - name: 'TestComponent', - __file: 'src/components/TestComponent.vue', - mixins: [createComponentDebugMixin()], - template: '
Test Content
' - } - - const wrapper = mount(TestComponent, { - attachTo: document.body - }) - - const element = wrapper.element - const parent = element.parentNode - - // Verify comments exist - expect(element.previousSibling?.nodeType).toBe(Node.COMMENT_NODE) - expect(element.nextSibling?.nodeType).toBe(Node.COMMENT_NODE) - - // Store references to comments - const startComment = element.previousSibling - const endComment = element.nextSibling - - // Unmount component - wrapper.unmount() - - // Verify comments are removed from DOM - expect(parent.contains(startComment)).toBe(false) - expect(parent.contains(endComment)).toBe(false) - }) - - it('handles multiple components with different names', () => { - process.env.NODE_ENV = 'development' - - const Component1 = { - __file: 'src/components/Component1.vue', - mixins: [createComponentDebugMixin()], - template: '
Component 1
' - } - - const Component2 = { - __file: 'src/components/Component2.vue', - mixins: [createComponentDebugMixin()], - template: '
Component 2
' - } - - const wrapper1 = mount(Component1, { attachTo: document.body }) - const wrapper2 = mount(Component2, { attachTo: document.body }) - - expect(wrapper1.element.previousSibling.nodeValue).toBe(' Start component: src/components/Component1.vue ') - expect(wrapper2.element.previousSibling.nodeValue).toBe(' Start component: src/components/Component2.vue ') - - wrapper1.unmount() - wrapper2.unmount() - }) - - it('handles components without parent nodes gracefully', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - __file: 'src/components/TestComponent.vue', - mixins: [createComponentDebugMixin()], - template: '
Test Content
' - } - - // Mount without attaching to document - const wrapper = mount(TestComponent) - - // Should not throw errors when parent is null - expect(() => { - wrapper.unmount() - }).not.toThrow() - }) - - it('only removes matching comments during cleanup', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - __file: 'src/components/TestComponent.vue', - mixins: [createComponentDebugMixin()], - template: '
Test Content
' - } - - const container = document.createElement('div') - document.body.appendChild(container) - - // Add some unrelated comments - const unrelatedComment1 = document.createComment(' Some other comment ') - const unrelatedComment2 = document.createComment(' Another comment ') - container.appendChild(unrelatedComment1) - container.appendChild(unrelatedComment2) - - const wrapper = mount(TestComponent, { - attachTo: container - }) - - // Verify our component comments exist - const element = wrapper.element - expect(element.previousSibling.nodeValue).toBe(' Start component: src/components/TestComponent.vue ') - expect(element.nextSibling.nodeValue).toBe(' End component: src/components/TestComponent.vue ') - - wrapper.unmount() - - // Unrelated comments should still exist - expect(container.contains(unrelatedComment1)).toBe(true) - expect(container.contains(unrelatedComment2)).toBe(true) - - document.body.removeChild(container) - }) -}) \ No newline at end of file + let originalEnv; + + beforeEach(() => { + originalEnv = process.env.NODE_ENV; + }); + + afterEach(() => { + process.env.NODE_ENV = originalEnv; + }); + + it('creates a mixin with mounted and beforeUnmount hooks', () => { + const mixin = createComponentDebugMixin(); + + expect(mixin).toHaveProperty('mounted'); + expect(mixin).toHaveProperty('beforeUnmount'); + expect(typeof mixin.mounted).toBe('function'); + expect(typeof mixin.beforeUnmount).toBe('function'); + }); + + it('adds HTML comments around component in development mode', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + name: 'TestComponent', + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
', + }; + + const wrapper = mount(TestComponent, { + attachTo: document.body, + }); + + const element = wrapper.element; + const parent = element.parentNode; + + // Check for start comment + const prevSibling = element.previousSibling; + expect(prevSibling).toBeTruthy(); + expect(prevSibling.nodeType).toBe(Node.COMMENT_NODE); + expect(prevSibling.nodeValue).toBe(' Start component: src/components/TestComponent.vue '); + + // Check for end comment + const nextSibling = element.nextSibling; + expect(nextSibling).toBeTruthy(); + expect(nextSibling.nodeType).toBe(Node.COMMENT_NODE); + expect(nextSibling.nodeValue).toBe(' End component: src/components/TestComponent.vue '); + + wrapper.unmount(); + }); + + it('uses component name when __file is not available', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + __name: 'MyComponent', + mixins: [createComponentDebugMixin()], + template: '
Test Content
', + }; + + const wrapper = mount(TestComponent, { + attachTo: document.body, + }); + + const element = wrapper.element; + const prevSibling = element.previousSibling; + + expect(prevSibling.nodeValue).toBe(' Start component: MyComponent '); + + wrapper.unmount(); + }); + + it('uses "Anonymous" when no component identifier is available', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + mixins: [createComponentDebugMixin()], + template: '
Test Content
', + }; + + const wrapper = mount(TestComponent, { + attachTo: document.body, + }); + + const element = wrapper.element; + const prevSibling = element.previousSibling; + + expect(prevSibling.nodeValue).toBe(' Start component: Anonymous '); + + wrapper.unmount(); + }); + + it('does not add comments in production mode', () => { + process.env.NODE_ENV = 'production'; + + const TestComponent = { + name: 'TestComponent', + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
', + }; + + const wrapper = mount(TestComponent, { + attachTo: document.body, + }); + + const element = wrapper.element; + + // Should not have comment siblings in production + expect(element.previousSibling).toBeNull(); + expect(element.nextSibling).toBeNull(); + + wrapper.unmount(); + }); + + it('cleans up comments when component is unmounted', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + name: 'TestComponent', + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
', + }; + + const wrapper = mount(TestComponent, { + attachTo: document.body, + }); + + const element = wrapper.element; + const parent = element.parentNode; + + // Verify comments exist + expect(element.previousSibling?.nodeType).toBe(Node.COMMENT_NODE); + expect(element.nextSibling?.nodeType).toBe(Node.COMMENT_NODE); + + // Store references to comments + const startComment = element.previousSibling; + const endComment = element.nextSibling; + + // Unmount component + wrapper.unmount(); + + // Verify comments are removed from DOM + expect(parent.contains(startComment)).toBe(false); + expect(parent.contains(endComment)).toBe(false); + }); + + it('handles multiple components with different names', () => { + process.env.NODE_ENV = 'development'; + + const Component1 = { + __file: 'src/components/Component1.vue', + mixins: [createComponentDebugMixin()], + template: '
Component 1
', + }; + + const Component2 = { + __file: 'src/components/Component2.vue', + mixins: [createComponentDebugMixin()], + template: '
Component 2
', + }; + + const wrapper1 = mount(Component1, { attachTo: document.body }); + const wrapper2 = mount(Component2, { attachTo: document.body }); + + expect(wrapper1.element.previousSibling.nodeValue).toBe(' Start component: src/components/Component1.vue '); + expect(wrapper2.element.previousSibling.nodeValue).toBe(' Start component: src/components/Component2.vue '); + + wrapper1.unmount(); + wrapper2.unmount(); + }); + + it('handles components without parent nodes gracefully', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
', + }; + + // Mount without attaching to document + const wrapper = mount(TestComponent); + + // Should not throw errors when parent is null + expect(() => { + wrapper.unmount(); + }).not.toThrow(); + }); + + it('only removes matching comments during cleanup', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + __file: 'src/components/TestComponent.vue', + mixins: [createComponentDebugMixin()], + template: '
Test Content
', + }; + + const container = document.createElement('div'); + document.body.appendChild(container); + + // Add some unrelated comments + const unrelatedComment1 = document.createComment(' Some other comment '); + const unrelatedComment2 = document.createComment(' Another comment '); + container.appendChild(unrelatedComment1); + container.appendChild(unrelatedComment2); + + const wrapper = mount(TestComponent, { + attachTo: container, + }); + + // Verify our component comments exist + const element = wrapper.element; + expect(element.previousSibling.nodeValue).toBe(' Start component: src/components/TestComponent.vue '); + expect(element.nextSibling.nodeValue).toBe(' End component: src/components/TestComponent.vue '); + + wrapper.unmount(); + + // Unrelated comments should still exist + expect(container.contains(unrelatedComment1)).toBe(true); + expect(container.contains(unrelatedComment2)).toBe(true); + + document.body.removeChild(container); + }); +}); diff --git a/tests/plugin.test.js b/tests/plugin.test.js index 88bbedf..564d3dd 100644 --- a/tests/plugin.test.js +++ b/tests/plugin.test.js @@ -1,189 +1,189 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' -import { createApp } from 'vue' -import { mount } from '@vue/test-utils' -import VueComponentDebug from '../src/index.js' +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { createApp } from 'vue'; +import { mount } from '@vue/test-utils'; +import VueComponentDebug from '../src/index.js'; describe('VueComponentDebug Plugin', () => { - let originalEnv - - beforeEach(() => { - originalEnv = process.env.NODE_ENV - }) - - afterEach(() => { - process.env.NODE_ENV = originalEnv - }) - - it('installs plugin and adds mixin to app', () => { - const app = createApp({}) - const mockMixin = vi.fn() - app.mixin = mockMixin - - VueComponentDebug.install(app) - - expect(mockMixin).toHaveBeenCalledWith( - expect.objectContaining({ - mounted: expect.any(Function), - beforeUnmount: expect.any(Function) - }) - ) - }) - - it('adds debug comments to all components when installed globally in development', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - name: 'TestComponent', - template: '
Test
' - } - - // Mount with global plugin - const wrapper = mount(TestComponent, { - attachTo: document.body, - global: { - plugins: [VueComponentDebug] - } - }) - - const element = wrapper.element - - // Check for debug comments (will use "Anonymous" since __file is not available in test) - expect(element.previousSibling?.nodeType).toBe(Node.COMMENT_NODE) - expect(element.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') - expect(element.nextSibling?.nodeType).toBe(Node.COMMENT_NODE) - expect(element.nextSibling?.nodeValue).toBe(' End component: Anonymous ') - - wrapper.unmount() - }) - - it('works with multiple components', () => { - process.env.NODE_ENV = 'development' - - const Component1 = { - template: '
Component 1
' - } - - const Component2 = { - template: '
Component 2
' - } - - const wrapper1 = mount(Component1, { - attachTo: document.body, - global: { - plugins: [VueComponentDebug] - } - }) - - const wrapper2 = mount(Component2, { - attachTo: document.body, - global: { - plugins: [VueComponentDebug] - } - }) - - // Both should use "Anonymous" since no component identifier is available in test environment - expect(wrapper1.element.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') - expect(wrapper2.element.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') - - wrapper1.unmount() - wrapper2.unmount() - }) - - it('does not add comments in production mode', () => { - process.env.NODE_ENV = 'production' - - const TestComponent = { - name: 'TestComponent', - __file: 'src/components/TestComponent.vue', - template: '
Test
' - } - - const wrapper = mount(TestComponent, { - attachTo: document.body, - global: { - plugins: [VueComponentDebug] - } - }) - - const element = wrapper.element - - // Should not have comment siblings in production - expect(element.previousSibling).toBeNull() - expect(element.nextSibling).toBeNull() - - wrapper.unmount() - }) - - it('handles app parameter gracefully when undefined', () => { - // Test that the plugin throws when app doesn't have mixin method - expect(() => { - VueComponentDebug.install() - }).toThrow('app.mixin is not a function') - }) - - it('cleans up comments when components are unmounted globally', () => { - process.env.NODE_ENV = 'development' - - const TestComponent = { - __file: 'src/components/TestComponent.vue', - template: '
Test Content
' - } - - const wrapper = mount(TestComponent, { - attachTo: document.body, - global: { - plugins: [VueComponentDebug] - } - }) - - const element = wrapper.element - const parent = element.parentNode - - // Store references to comments - const startComment = element.previousSibling - const endComment = element.nextSibling - - // Verify comments exist - expect(startComment?.nodeType).toBe(Node.COMMENT_NODE) - expect(endComment?.nodeType).toBe(Node.COMMENT_NODE) - - // Unmount component - wrapper.unmount() - - // Verify comments are removed from DOM - expect(parent.contains(startComment)).toBe(false) - expect(parent.contains(endComment)).toBe(false) - }) - - it('works with nested components', () => { - process.env.NODE_ENV = 'development' - - const ChildComponent = { - template: 'Child' - } - - const ParentComponent = { - components: { ChildComponent }, - template: '
Parent
' - } - - const wrapper = mount(ParentComponent, { - attachTo: document.body, - global: { - plugins: [VueComponentDebug] - } - }) - - // Check parent component comments (will use "Anonymous" in test environment) - const parentElement = wrapper.element - expect(parentElement.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') - expect(parentElement.nextSibling?.nodeValue).toBe(' End component: Anonymous ') - - // Check child component comments - const childElement = wrapper.findComponent(ChildComponent).element - expect(childElement.previousSibling?.nodeValue).toBe(' Start component: Anonymous ') - expect(childElement.nextSibling?.nodeValue).toBe(' End component: Anonymous ') - - wrapper.unmount() - }) -}) \ No newline at end of file + let originalEnv; + + beforeEach(() => { + originalEnv = process.env.NODE_ENV; + }); + + afterEach(() => { + process.env.NODE_ENV = originalEnv; + }); + + it('installs plugin and adds mixin to app', () => { + const app = createApp({}); + const mockMixin = vi.fn(); + app.mixin = mockMixin; + + VueComponentDebug.install(app); + + expect(mockMixin).toHaveBeenCalledWith( + expect.objectContaining({ + mounted: expect.any(Function), + beforeUnmount: expect.any(Function), + }), + ); + }); + + it('adds debug comments to all components when installed globally in development', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + name: 'TestComponent', + template: '
Test
', + }; + + // Mount with global plugin + const wrapper = mount(TestComponent, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug], + }, + }); + + const element = wrapper.element; + + // Check for debug comments (will use "Anonymous" since __file is not available in test) + expect(element.previousSibling?.nodeType).toBe(Node.COMMENT_NODE); + expect(element.previousSibling?.nodeValue).toBe(' Start component: Anonymous '); + expect(element.nextSibling?.nodeType).toBe(Node.COMMENT_NODE); + expect(element.nextSibling?.nodeValue).toBe(' End component: Anonymous '); + + wrapper.unmount(); + }); + + it('works with multiple components', () => { + process.env.NODE_ENV = 'development'; + + const Component1 = { + template: '
Component 1
', + }; + + const Component2 = { + template: '
Component 2
', + }; + + const wrapper1 = mount(Component1, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug], + }, + }); + + const wrapper2 = mount(Component2, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug], + }, + }); + + // Both should use "Anonymous" since no component identifier is available in test environment + expect(wrapper1.element.previousSibling?.nodeValue).toBe(' Start component: Anonymous '); + expect(wrapper2.element.previousSibling?.nodeValue).toBe(' Start component: Anonymous '); + + wrapper1.unmount(); + wrapper2.unmount(); + }); + + it('does not add comments in production mode', () => { + process.env.NODE_ENV = 'production'; + + const TestComponent = { + name: 'TestComponent', + __file: 'src/components/TestComponent.vue', + template: '
Test
', + }; + + const wrapper = mount(TestComponent, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug], + }, + }); + + const element = wrapper.element; + + // Should not have comment siblings in production + expect(element.previousSibling).toBeNull(); + expect(element.nextSibling).toBeNull(); + + wrapper.unmount(); + }); + + it('handles app parameter gracefully when undefined', () => { + // Test that the plugin throws when app doesn't have mixin method + expect(() => { + VueComponentDebug.install(); + }).toThrow('app.mixin is not a function'); + }); + + it('cleans up comments when components are unmounted globally', () => { + process.env.NODE_ENV = 'development'; + + const TestComponent = { + __file: 'src/components/TestComponent.vue', + template: '
Test Content
', + }; + + const wrapper = mount(TestComponent, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug], + }, + }); + + const element = wrapper.element; + const parent = element.parentNode; + + // Store references to comments + const startComment = element.previousSibling; + const endComment = element.nextSibling; + + // Verify comments exist + expect(startComment?.nodeType).toBe(Node.COMMENT_NODE); + expect(endComment?.nodeType).toBe(Node.COMMENT_NODE); + + // Unmount component + wrapper.unmount(); + + // Verify comments are removed from DOM + expect(parent.contains(startComment)).toBe(false); + expect(parent.contains(endComment)).toBe(false); + }); + + it('works with nested components', () => { + process.env.NODE_ENV = 'development'; + + const ChildComponent = { + template: 'Child', + }; + + const ParentComponent = { + components: { ChildComponent }, + template: '
Parent
', + }; + + const wrapper = mount(ParentComponent, { + attachTo: document.body, + global: { + plugins: [VueComponentDebug], + }, + }); + + // Check parent component comments (will use "Anonymous" in test environment) + const parentElement = wrapper.element; + expect(parentElement.previousSibling?.nodeValue).toBe(' Start component: Anonymous '); + expect(parentElement.nextSibling?.nodeValue).toBe(' End component: Anonymous '); + + // Check child component comments + const childElement = wrapper.findComponent(ChildComponent).element; + expect(childElement.previousSibling?.nodeValue).toBe(' Start component: Anonymous '); + expect(childElement.nextSibling?.nodeValue).toBe(' End component: Anonymous '); + + wrapper.unmount(); + }); +}); diff --git a/vite.config.js b/vite.config.js index 7cecd86..7c50aea 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,27 +1,27 @@ -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' -import { resolve } from 'path' +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import { resolve } from 'path'; export default defineConfig({ - plugins: [vue()], - build: { - lib: { - entry: resolve(__dirname, 'src/index.js'), - name: 'VueComponentDebug', - formats: ['es', 'umd'], - fileName: (format) => `vue-component-debug.${format === 'es' ? 'js' : 'umd.cjs'}`, - }, - rollupOptions: { - external: ['vue'], - output: { - globals: { - vue: 'Vue', + plugins: [vue()], + build: { + lib: { + entry: resolve(__dirname, 'src/index.js'), + name: 'VueComponentDebug', + formats: ['es', 'umd'], + fileName: (format) => `vue-component-debug.${format === 'es' ? 'js' : 'umd.cjs'}`, + }, + rollupOptions: { + external: ['vue'], + output: { + globals: { + vue: 'Vue', + }, + }, }, - }, }, - }, - test: { - environment: 'jsdom', - globals: true - }, -}) + test: { + environment: 'jsdom', + globals: true, + }, +});