diff --git a/.eslintrc.json b/.eslintrc.json index ff73f97..2e38e93 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,8 +4,7 @@ "es2022": true }, "extends": [ - "eslint:recommended", - "@typescript-eslint/recommended" + "eslint:recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 02cbd88..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,39 +0,0 @@ -version: 2 -updates: - # Enable version updates for npm - - package-ecosystem: "npm" - directory: "/" - schedule: - interval: "weekly" - day: "monday" - time: "09:00" - open-pull-requests-limit: 10 - reviewers: - - "Utsav-Virani" - assignees: - - "Utsav-Virani" - commit-message: - prefix: "deps" - include: "scope" - labels: - - "dependencies" - - "automated" - - # Enable version updates for GitHub Actions - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - day: "monday" - time: "09:00" - open-pull-requests-limit: 5 - reviewers: - - "Utsav-Virani" - assignees: - - "Utsav-Virani" - commit-message: - prefix: "ci" - include: "scope" - labels: - - "github-actions" - - "automated" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 562b5d3..aedebc2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main, develop ] + branches: [ master, develop, release/* ] pull_request: - branches: [ main, develop ] + branches: [ master, develop ] jobs: test: diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 3ceba01..8c68a55 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -19,11 +19,11 @@ jobs: uses: actions/checkout@v4 - name: Dependency Review - uses: actions/dependency-review-action@v3 + uses: actions/dependency-review-action@v4 with: fail-on-severity: high - allow-dependencies-licenses: MIT, Apache-2.0, BSD-3-Clause, ISC, BSD-2-Clause - deny-licenses: GPL-3.0, LGPL-3.0, AGPL-3.0 + allow-licenses: MIT, Apache-2.0, BSD-3-Clause, ISC, BSD-2-Clause + # deny-licenses: GPL-3.0, LGPL-3.0, AGPL-3.0 - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/tag-and-release.yml b/.github/workflows/tag-and-release.yml index 8398914..d297f5e 100644 --- a/.github/workflows/tag-and-release.yml +++ b/.github/workflows/tag-and-release.yml @@ -2,7 +2,7 @@ name: Tag and Release on: push: - branches: [ main ] + branches: [ master ] paths: [ 'package.json' ] jobs: diff --git a/.gitignore b/.gitignore index 76add87..881e8b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules -dist \ No newline at end of file +dist + + +__temp__ diff --git a/RELEASE_CHECKLIST_v1.1.0.md b/RELEASE_CHECKLIST_v1.1.0.md new file mode 100644 index 0000000..ea269ff --- /dev/null +++ b/RELEASE_CHECKLIST_v1.1.0.md @@ -0,0 +1,231 @@ +# Release Checklist v1.1.0 + +## Version Comparison + +### Current State (v1.0.1) āœ… **RELEASED** +- [x] Fixed CLI executable issue (critical bug fix) +- [x] Added `--verbose` and `--dry-run` flags +- [x] Enhanced error handling and validation +- [x] Improved type inference for arrays and objects +- [x] Added chalk colors for better UX +- [x] Comprehensive documentation (README, CONTRIBUTING, etc.) +- [x] Professional automation (GitHub Actions, Dependabot) +- [x] Published to npm as v1.0.1 + +### Planned v1.1.0 Features (Future) + +#### šŸŽÆ Major Features +- [ ] **Nested Object Interface Generation** + - Generate proper TypeScript interfaces for nested objects + - Support for complex object structures + - Better type inference for nested properties + +- [ ] **Testing Infrastructure** + - Unit tests for core functionality + - Integration tests for CLI commands + - Test coverage reporting + - CI/CD test automation + +- [ ] **Configuration File Support** + - `.react-state-cli.json` configuration file + - Customizable output directories + - Template customization options + - Naming convention preferences + +#### šŸ”§ Enhanced Features +- [ ] **Advanced Type Inference** + - Better handling of arrays with specific types + - Union types for mixed arrays + - Optional property detection + - Enum generation for string literals + +- [ ] **Template System** + - User-defined custom templates + - Template inheritance + - Plugin system for extensions + - Multiple template formats + +- [ ] **Multi-Slice Generation** + - Batch processing of multiple initState files + - Workspace-wide Redux setup + - Automatic store configuration + - Index file generation + +#### šŸ› ļø Developer Experience +- [ ] **VS Code Extension** + - Syntax highlighting for templates + - IntelliSense for generated code + - Right-click context menu + - Live preview of generated code + +- [ ] **Enhanced CLI** + - Interactive mode for configuration + - Progress bars for long operations + - Better error messages with suggestions + - Help system with examples + +#### šŸ“Š Quality & Performance +- [ ] **Performance Optimizations** + - Faster file processing + - Memory usage optimization + - Parallel processing for multiple files + - Caching for repeated operations + +- [ ] **Code Quality** + - ESLint rules for generated code + - Prettier formatting options + - Code quality metrics + - Documentation generation + +## Development Timeline + +### Phase 1: Foundation (Week 1-2) +- [ ] Set up comprehensive testing infrastructure +- [ ] Implement nested object interface generation +- [ ] Add configuration file support +- [ ] Enhance type inference capabilities + +### Phase 2: Advanced Features (Week 3-4) +- [ ] Custom template system +- [ ] Multi-slice generation +- [ ] VS Code extension development +- [ ] Performance optimizations + +### Phase 3: Polish & Release (Week 5-6) +- [ ] Documentation updates +- [ ] Migration guide from v1.0.1 +- [ ] Beta testing with community +- [ ] Final release preparations + +## Technical Requirements + +### Dependencies to Add +- [ ] `jest` - Testing framework +- [ ] `@types/jest` - TypeScript support for Jest +- [ ] `ts-jest` - TypeScript preprocessor for Jest +- [ ] `inquirer` - Interactive CLI prompts +- [ ] `glob` - File pattern matching +- [ ] `yaml` - YAML configuration support + +### Breaking Changes (if any) +- [ ] None planned - maintain backward compatibility +- [ ] Configuration file is optional +- [ ] New features are additive + +### Migration Guide +- [ ] v1.0.1 → v1.1.0 should be seamless +- [ ] New features are opt-in +- [ ] Existing generated code remains valid +- [ ] Configuration file provides new capabilities + +## Testing Strategy + +### Unit Tests +- [ ] Core generation logic +- [ ] Type inference functions +- [ ] Template processing +- [ ] Configuration parsing +- [ ] Error handling + +### Integration Tests +- [ ] CLI command execution +- [ ] File system operations +- [ ] Template rendering +- [ ] Multi-file generation +- [ ] Configuration file loading + +### End-to-End Tests +- [ ] Complete workflow testing +- [ ] Real project integration +- [ ] Performance benchmarks +- [ ] Cross-platform compatibility + +## Release Criteria + +### Must Have +- [ ] All planned features implemented +- [ ] Comprehensive test coverage (>90%) +- [ ] Documentation updated +- [ ] No breaking changes +- [ ] Performance regression testing + +### Nice to Have +- [ ] VS Code extension beta +- [ ] Community feedback integration +- [ ] Advanced template examples +- [ ] Video tutorials + +## Post-Release Tasks + +### Immediate (Week 1) +- [ ] Monitor npm download metrics +- [ ] Address any critical issues +- [ ] Community support and feedback +- [ ] Documentation improvements + +### Short-term (Month 1) +- [ ] VS Code extension public release +- [ ] Community template sharing +- [ ] Performance monitoring +- [ ] Feature usage analytics + +### Long-term (Month 2-3) +- [ ] Plan v1.2.0 features +- [ ] Expand ecosystem integrations +- [ ] Conference talks and presentations +- [ ] Partnership opportunities + +## Success Metrics + +### Downloads & Usage +- [ ] Target: 50% increase in weekly downloads +- [ ] Target: 100+ GitHub stars +- [ ] Target: 10+ community contributions +- [ ] Target: 5+ community templates + +### Quality Metrics +- [ ] Target: <5 critical issues reported +- [ ] Target: <24hr average issue response time +- [ ] Target: 95%+ test coverage maintained +- [ ] Target: <2sec average generation time + +### Community Growth +- [ ] Target: 20+ active contributors +- [ ] Target: 500+ Discord/community members +- [ ] Target: 10+ external blog posts/reviews +- [ ] Target: 5+ integration examples + +--- + +## Notes + +### v1.0.1 Success Summary +- āœ… Successfully published to npm +- āœ… All automation working (GitHub Actions, Dependabot) +- āœ… Professional documentation complete +- āœ… Community ready for contributions +- āœ… Solid foundation for future growth + +### v1.1.0 Focus Areas +- šŸŽÆ **Quality First**: Comprehensive testing before new features +- šŸ”§ **Developer Experience**: Make it easier and more powerful to use +- šŸ“Š **Performance**: Ensure it scales well with larger projects +- šŸ¤ **Community**: Build ecosystem around the tool + +### Development Approach +- **Incremental Development**: Small, testable changes +- **Community Feedback**: Regular beta releases for testing +- **Backward Compatibility**: Never break existing workflows +- **Documentation First**: Document features before implementation + +### Risk Mitigation +- **Feature Creep**: Stick to planned scope +- **Breaking Changes**: Avoid at all costs +- **Performance**: Regular benchmarking +- **Community**: Maintain active communication + +--- + +**Estimated Development Time: 4-6 weeks** +**Target Release Date: Q2 2025** +**Confidence Level: High** (based on v1.0.1 success) \ No newline at end of file diff --git a/examples/src/redux/examples/reducers.ts b/examples/src/redux/examples/reducers.ts new file mode 100644 index 0000000..60ad73a --- /dev/null +++ b/examples/src/redux/examples/reducers.ts @@ -0,0 +1,3 @@ +import reducer from './slice' + +export default reducer diff --git a/examples/src/redux/examples/slice.ts b/examples/src/redux/examples/slice.ts new file mode 100644 index 0000000..a694f3f --- /dev/null +++ b/examples/src/redux/examples/slice.ts @@ -0,0 +1,107 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { ExamplesState } from './types' + +/** + * Initial state for the examples slice + */ +const initialState: ExamplesState = { + "products": [], + "loading": false, + "error": null, + "selectedProductId": 0, + "searchQuery": "", + "currentPage": 1, + "itemsPerPage": 10, + "totalItems": 0, + "filters": { + "category": "", + "priceRange": [ + 0, + 1000 + ], + "inStock": true, + "sortBy": "name", + "sortOrder": "asc" + }, + "isFiltersOpen": false, + "selectedView": "grid", + "editingProduct": null, + "isDirty": false, + "categories": [ + "electronics", + "clothing", + "books" + ], + "recentlyViewed": [], + "lastUpdated": null, + "isInitialized": false, + "debugMode": false +} + +/** + * Redux slice for examples state management + * Auto-generated by react-state-cli + */ +const examplesSlice = createSlice({ + name: 'examples', + initialState, + reducers: { + setProducts: (state, action: PayloadAction) => { + state.products = action.payload + }, + setLoading: (state, action: PayloadAction) => { + state.loading = action.payload + }, + setError: (state, action: PayloadAction) => { + state.error = action.payload + }, + setSelectedProductId: (state, action: PayloadAction) => { + state.selectedProductId = action.payload + }, + setSearchQuery: (state, action: PayloadAction) => { + state.searchQuery = action.payload + }, + setCurrentPage: (state, action: PayloadAction) => { + state.currentPage = action.payload + }, + setItemsPerPage: (state, action: PayloadAction) => { + state.itemsPerPage = action.payload + }, + setTotalItems: (state, action: PayloadAction) => { + state.totalItems = action.payload + }, + setFilters: (state, action: PayloadAction>) => { + state.filters = action.payload + }, + setIsFiltersOpen: (state, action: PayloadAction) => { + state.isFiltersOpen = action.payload + }, + setSelectedView: (state, action: PayloadAction) => { + state.selectedView = action.payload + }, + setEditingProduct: (state, action: PayloadAction) => { + state.editingProduct = action.payload + }, + setIsDirty: (state, action: PayloadAction) => { + state.isDirty = action.payload + }, + setCategories: (state, action: PayloadAction) => { + state.categories = action.payload + }, + setRecentlyViewed: (state, action: PayloadAction) => { + state.recentlyViewed = action.payload + }, + setLastUpdated: (state, action: PayloadAction) => { + state.lastUpdated = action.payload + }, + setIsInitialized: (state, action: PayloadAction) => { + state.isInitialized = action.payload + }, + setDebugMode: (state, action: PayloadAction) => { + state.debugMode = action.payload + }, + } +}) + +export const { setProducts, setLoading, setError, setSelectedProductId, setSearchQuery, setCurrentPage, setItemsPerPage, setTotalItems, setFilters, setIsFiltersOpen, setSelectedView, setEditingProduct, setIsDirty, setCategories, setRecentlyViewed, setLastUpdated, setIsInitialized, setDebugMode } = examplesSlice.actions +export default examplesSlice.reducer diff --git a/examples/src/redux/examples/types.ts b/examples/src/redux/examples/types.ts new file mode 100644 index 0000000..843db6b --- /dev/null +++ b/examples/src/redux/examples/types.ts @@ -0,0 +1,38 @@ +export interface ExamplesState { + /** Array with 0 initial items */ + products: any[] + /** boolean value */ + loading: boolean + /** Nullable value */ + error: null + /** number value */ + selectedProductId: number + /** string value */ + searchQuery: string + /** number value */ + currentPage: number + /** number value */ + itemsPerPage: number + /** number value */ + totalItems: number + /** Object with 5 properties */ + filters: Record + /** boolean value */ + isFiltersOpen: boolean + /** string value */ + selectedView: string + /** Nullable value */ + editingProduct: null + /** boolean value */ + isDirty: boolean + /** Array with 3 initial items */ + categories: string[] + /** Array with 0 initial items */ + recentlyViewed: any[] + /** Nullable value */ + lastUpdated: null + /** boolean value */ + isInitialized: boolean + /** boolean value */ + debugMode: boolean +} \ No newline at end of file diff --git a/examples/src/redux/state.ts b/examples/src/redux/state.ts new file mode 100644 index 0000000..46d45f4 --- /dev/null +++ b/examples/src/redux/state.ts @@ -0,0 +1,5 @@ +import { ExamplesState } from './examples/types' + +export interface RootState { + examples: ExamplesState +} diff --git a/jest.config.cjs b/jest.config.cjs new file mode 100644 index 0000000..8fdd16b --- /dev/null +++ b/jest.config.cjs @@ -0,0 +1,29 @@ +/** @type {import('jest').Config} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/__tests__'], + setupFilesAfterEnv: ['/jest.setup.cjs'], + testMatch: [ + '**/__tests__/**/*.test.ts', + '**/?(*.)+(spec|test).ts' + ], + transform: { + '^.+\\.ts$': ['ts-jest', { + useESM: false, + tsconfig: { + module: 'commonjs', + target: 'es2020' + } + }] + }, + collectCoverageFrom: [ + 'src/**/*.ts', + '!src/**/*.d.ts' + ], + moduleFileExtensions: ['ts', 'js'], + testPathIgnorePatterns: [ + '/node_modules/', + '/dist/' + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6897719..6142287 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,7 @@ "ejs": "^3.1.10", "fs-extra": "^11.3.0", "ts-morph": "^26.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.8.3" + "ts-node": "^10.9.2" }, "bin": { "react-state-cli": "dist/index.js" @@ -24,10 +23,14 @@ "devDependencies": { "@types/ejs": "^3.1.5", "@types/fs-extra": "^11.0.4", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.0.0", - "prettier": "^3.0.0" + "@types/node": "^24.0.10", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "eslint": "^8.57.0", + "prettier": "^3.0.0", + "tsx": "^4.20.3", + "typescript": "^5.8.3", + "vitest": "^3.2.4" } }, "node_modules/@cspotcode/source-map-support": { @@ -42,6 +45,448 @@ "node": ">=12" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -95,6 +540,30 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", @@ -121,6 +590,30 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -250,6 +743,286 @@ } } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", + "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", + "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", + "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", + "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", + "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", + "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", + "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", + "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", + "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", + "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", + "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", + "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", + "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", + "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", + "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", + "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", + "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", + "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", + "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", + "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -312,6 +1085,23 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ejs": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", @@ -319,6 +1109,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/fs-extra": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", @@ -330,12 +1127,6 @@ "@types/node": "*" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, "node_modules/@types/jsonfile": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", @@ -355,39 +1146,31 @@ "undici-types": "~7.8.0" } }, - "node_modules/@types/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", - "dev": true - }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.0.tgz", - "integrity": "sha512-M72SJ0DkcQVmmsbqlzc6EJgb/3Oz2Wdm6AyESB4YkGgCxP8u5jt5jn4/OBMPK3HLOxcttZq5xbBBU7e2By4SZQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.0.0", - "@typescript-eslint/type-utils": "7.0.0", - "@typescript-eslint/utils": "7.0.0", - "@typescript-eslint/visitor-keys": "7.0.0", - "debug": "^4.3.4", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "@typescript-eslint/parser": "^7.0.0", "eslint": "^8.56.0" }, "peerDependenciesMeta": { @@ -444,27 +1227,27 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -473,17 +1256,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -491,18 +1274,19 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.0.tgz", - "integrity": "sha512-FIM8HPxj1P2G7qfrpiXvbHeHypgo2mFpFGoh5I73ZlqmJOsloSa1x0ZyXCer43++P1doxCgNqIOLqmZR6SOT8g==", + + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.0.0", - "@typescript-eslint/utils": "7.0.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -517,120 +1301,40 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.0.tgz", - "integrity": "sha512-9ZIJDqagK1TTs4W9IyeB2sH/s1fFhN9958ycW8NRTg1vXGzzH5PQNzq6KbsbVGMT+oyyfa17DfchHDidcmf5cg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.0.tgz", - "integrity": "sha512-JzsOzhJJm74aQ3c9um/aDryHgSHfaX8SHFIu9x4Gpik/+qxLvxUylhTsO9abcNu39JIdhY2LgYrFxTii3IajLA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.0.0", - "@typescript-eslint/visitor-keys": "7.0.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.0.tgz", - "integrity": "sha512-JZP0uw59PRHp7sHQl3aF/lFgwOW2rgNVnXUksj1d932PMita9wFBd3621vHQRDvHwPsSY9FMAAHVc8gTvLYY4w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.0.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, "license": "MIT", "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" + }, "funding": { "type": "opencollective", @@ -642,180 +1346,170 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "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/@typescript-eslint/utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.0.tgz", - "integrity": "sha512-kuPZcPAdGcDBAyqDn/JVeJVhySvpkxzfXjJq1X1BFSTYo1TTuo4iyb937u457q4K0In84p6u2VHQGaFnv7VYqg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.0.0", - "@typescript-eslint/types": "7.0.0", - "@typescript-eslint/typescript-estree": "7.0.0", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, "peerDependencies": { "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.0.tgz", - "integrity": "sha512-IxTStwhNDPO07CCrYuAqjuJ3Xf5MrMaNgbAZPxFXAUpAtwqFxiuItxUaVtP/SJQeCdJjwDGh9/lMOluAndkKeg==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.0.0", - "@typescript-eslint/visitor-keys": "7.0.0" + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.0.tgz", - "integrity": "sha512-9ZIJDqagK1TTs4W9IyeB2sH/s1fFhN9958ycW8NRTg1vXGzzH5PQNzq6KbsbVGMT+oyyfa17DfchHDidcmf5cg==", + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "license": "ISC" + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.0.tgz", - "integrity": "sha512-JzsOzhJJm74aQ3c9um/aDryHgSHfaX8SHFIu9x4Gpik/+qxLvxUylhTsO9abcNu39JIdhY2LgYrFxTii3IajLA==", + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.0.0", - "@typescript-eslint/visitor-keys": "7.0.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { - "typescript": { + "msw": { + "optional": true + }, + "vite": { "optional": true } } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.0.tgz", - "integrity": "sha512-JZP0uw59PRHp7sHQl3aF/lFgwOW2rgNVnXUksj1d932PMita9wFBd3621vHQRDvHwPsSY9FMAAHVc8gTvLYY4w==", + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.0.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "tinyrainbow": "^2.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/utils/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==", + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "tinyspy": "^4.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, "node_modules/acorn": { "version": "8.15.0", @@ -916,6 +1610,16 @@ "node": ">=8" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -929,13 +1633,12 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -950,6 +1653,16 @@ "node": ">=8" } }, + "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/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -960,6 +1673,23 @@ "node": ">=6" } }, + "node_modules/chai": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -972,6 +1702,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/code-block-writer": { "version": "13.0.3", "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", @@ -1050,6 +1790,16 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1107,6 +1857,55 @@ "node": ">=0.10.0" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1207,6 +2006,17 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1224,17 +2034,17 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { - "is-glob": "^4.0.3" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10.13.0" + "node": "*" } }, "node_modules/espree": { @@ -1291,6 +2101,16 @@ "node": ">=4.0" } }, + "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/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -1301,6 +2121,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1324,6 +2154,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1369,15 +2211,6 @@ "minimatch": "^5.0.1" } }, - "node_modules/filelist/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==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -1462,6 +2295,34 @@ "dev": true, "license": "ISC" }, + "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/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1485,15 +2346,40 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/globals": { @@ -1686,6 +2572,16 @@ "node": ">=10" } }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/jake/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1702,6 +2598,25 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/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/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1795,6 +2710,23 @@ "dev": true, "license": "MIT" }, + "node_modules/loupe": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "dev": true, + "license": "MIT" + }, + "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/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1824,15 +2756,19 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "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": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ms": { @@ -1842,6 +2778,25 @@ "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/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1968,6 +2923,30 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1980,6 +2959,35 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "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, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2067,6 +3075,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -2094,6 +3112,46 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", + "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", + "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.2", + "@rollup/rollup-android-arm64": "4.44.2", + "@rollup/rollup-darwin-arm64": "4.44.2", + "@rollup/rollup-darwin-x64": "4.44.2", + "@rollup/rollup-freebsd-arm64": "4.44.2", + "@rollup/rollup-freebsd-x64": "4.44.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", + "@rollup/rollup-linux-arm-musleabihf": "4.44.2", + "@rollup/rollup-linux-arm64-gnu": "4.44.2", + "@rollup/rollup-linux-arm64-musl": "4.44.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-musl": "4.44.2", + "@rollup/rollup-linux-s390x-gnu": "4.44.2", + "@rollup/rollup-linux-x64-gnu": "4.44.2", + "@rollup/rollup-linux-x64-musl": "4.44.2", + "@rollup/rollup-win32-arm64-msvc": "4.44.2", + "@rollup/rollup-win32-ia32-msvc": "4.44.2", + "@rollup/rollup-win32-x64-msvc": "4.44.2", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2153,6 +3211,13 @@ "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/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -2163,6 +3228,30 @@ "node": ">=8" } }, + "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/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2189,6 +3278,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2208,6 +3310,95 @@ "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/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2286,6 +3477,26 @@ } } }, + "node_modules/tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2356,6 +3567,218 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "license": "MIT" }, + "node_modules/vite": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.2.tgz", + "integrity": "sha512-hxdyZDY1CM6SNpKI4w4lcUc3Mtkd9ej4ECWVHSMrOdSinVc2zYOAppHeGc/hzmRo3pxM5blMzkuWHOJA/3NiFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.2", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2372,6 +3795,23 @@ "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/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 9774356..c3d4c52 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "dev": "tsc --watch", "clean": "rm -rf dist", "prepublishOnly": "npm run clean && npm run build", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "vitest", "lint": "eslint src --ext .ts", "lint:fix": "eslint src --ext .ts --fix", "format": "prettier --write \"src/**/*.ts\"", @@ -40,7 +40,7 @@ "bugs": { "url": "https://github.com/Utsav-Virani/react-state-cli/issues" }, - "type": "commonjs", + "type": "module", "dependencies": { "@reduxjs/toolkit": "^2.8.2", "chalk": "^5.4.1", @@ -48,18 +48,21 @@ "ejs": "^3.1.10", "fs-extra": "^11.3.0", "ts-morph": "^26.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.8.3" + "ts-node": "^10.9.2" }, "devDependencies": { "@types/ejs": "^3.1.5", "@types/fs-extra": "^11.0.4", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.0.0", - "prettier": "^3.0.0" + "@types/node": "^24.0.10", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "eslint": "^8.57.0", + "prettier": "^3.0.0", + "tsx": "^4.20.3", + "typescript": "^5.8.3", + "vitest": "^3.2.4" }, "bin": { "react-state-cli": "./dist/index.js" } -} \ No newline at end of file +} diff --git a/src/generate.ts b/src/generate.ts index d44a2fb..b411da1 100644 --- a/src/generate.ts +++ b/src/generate.ts @@ -1,181 +1,339 @@ -import path from "path"; -import fs from "fs-extra"; -import chalk from "chalk"; -import { parseInitState } from "./utils"; -import ejs from "ejs"; +import path from 'path' +import fs from 'fs-extra' +import chalk from 'chalk' +import { parseInitState } from './utils.js' +import ejs from 'ejs' +import { fileURLToPath } from 'url' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) export interface GenerateOptions { - verbose?: boolean; - dryRun?: boolean; + verbose?: boolean + dryRun?: boolean } -export async function generateFilesFromInitState( - initStatePath: string, +export const generateFilesFromInitState = async ( + initStatePath: string, options: GenerateOptions = {} -): Promise { +): Promise => { try { - const sliceDir = path.dirname(initStatePath); - const sliceName = path.basename(sliceDir); - const reduxDir = path.resolve(sliceDir, ".."); // goes to /redux - const stateFilePath = path.resolve(reduxDir, "state.ts"); + const originalSliceDir = path.dirname(initStatePath) + const sliceName = path.basename(originalSliceDir) + + // Determine Redux structure and slice directory + let stateFilePath: string + let reduxRootDir: string + let sliceDir: string + + // Check if we're already in a Redux structure + const parentDir = path.dirname(originalSliceDir) + const parentDirName = path.basename(parentDir) + + if (parentDirName === 'redux' || parentDirName === 'store') { + // We're in src/redux/user/ structure - keep existing structure + reduxRootDir = parentDir + sliceDir = originalSliceDir + stateFilePath = path.resolve(reduxRootDir, 'state.ts') + } else { + // We need to create/find the redux structure + // Look for existing src/redux or create it + const cwd = process.cwd() + const srcReduxPath = path.resolve(cwd, 'src', 'redux') + const srcStorePath = path.resolve(cwd, 'src', 'store') + + if (await fs.pathExists(srcReduxPath)) { + reduxRootDir = srcReduxPath + } else if (await fs.pathExists(srcStorePath)) { + reduxRootDir = srcStorePath + } else { + // Create src/redux structure + reduxRootDir = srcReduxPath + await fs.ensureDir(reduxRootDir) + if (options.verbose) { + console.log(chalk.blue(`šŸ“ Created Redux directory: ${reduxRootDir}`)) + } + } + + // Create the slice directory in the Redux structure + sliceDir = path.resolve(reduxRootDir, sliceName) + await fs.ensureDir(sliceDir) + stateFilePath = path.resolve(reduxRootDir, 'state.ts') + } if (options.verbose) { - console.log(chalk.blue(`šŸ“‚ Slice directory: ${sliceDir}`)); - console.log(chalk.blue(`šŸ“ Slice name: ${sliceName}`)); + console.log(chalk.blue(`šŸ“‚ Original directory: ${originalSliceDir}`)) + console.log(chalk.blue(`šŸ“‚ Slice directory: ${sliceDir}`)) + console.log(chalk.blue(`šŸ“ Slice name: ${sliceName}`)) + console.log(chalk.blue(`šŸ“ Redux root directory: ${reduxRootDir}`)) + console.log(chalk.blue(`šŸ“„ State file path: ${stateFilePath}`)) } - const initState = await parseInitState(initStatePath); - const keys = Object.keys(initState); + const initState = await parseInitState(initStatePath) + if (!initState || Object.keys(initState).length === 0) { + throw new Error('Initial state object is empty') + } + const keys = Object.keys(initState) if (keys.length === 0) { - throw new Error("Initial state object is empty"); + throw new Error('Initial state object is empty') } // --- Generate slice.ts --- const actionCases = keys .map( - (key) => + key => ` set${capitalize( key )}: (state, action: PayloadAction<${inferType(initState[key])}>) => { state.${key} = action.payload },` ) - .join("\n"); + .join('\n') - const actionExports = keys.map((key) => `set${capitalize(key)}`).join(", "); + const actionExports = keys.map(key => `set${capitalize(key)}`).join(', ') const sliceTemplate = await fs.readFile( - path.resolve(__dirname, "../templates/slice.ts.tpl"), - "utf-8" - ); - const interfaceName = `${capitalize(sliceName)}State`; + path.resolve(__dirname, '../templates/slice.ts.tpl'), + 'utf-8' + ) + const interfaceName = `${capitalize(sliceName)}State` const renderedSlice = ejs.render(sliceTemplate, { sliceName, initialStateString: JSON.stringify(initState, null, 2), actionCases, actionExports, - interfaceName, - }); + interfaceName + }) if (options.dryRun) { - console.log(chalk.yellow("šŸ” [DRY RUN] Would generate slice.ts:")); - console.log(chalk.gray(renderedSlice)); + console.log(chalk.yellow('šŸ” [DRY RUN] Would generate slice.ts:')) + console.log(chalk.gray(renderedSlice)) } else { - await fs.writeFile(path.resolve(sliceDir, "slice.ts"), renderedSlice); - console.log(chalk.green(`āœ… slice.ts generated at ${sliceDir}`)); + await fs.writeFile(path.resolve(sliceDir, 'slice.ts'), renderedSlice) + console.log(chalk.green(`āœ… slice.ts generated at ${sliceDir}`)) } // --- Generate reducers.ts --- const reducersTemplate = await fs.readFile( - path.resolve(__dirname, "../templates/reducers.ts.tpl"), - "utf-8" - ); + path.resolve(__dirname, '../templates/reducers.ts.tpl'), + 'utf-8' + ) if (options.dryRun) { - console.log(chalk.yellow("šŸ” [DRY RUN] Would generate reducers.ts")); + console.log(chalk.yellow('šŸ” [DRY RUN] Would generate reducers.ts')) } else { - await fs.writeFile(path.resolve(sliceDir, "reducers.ts"), reducersTemplate); - console.log(chalk.green(`āœ… reducers.ts generated at ${sliceDir}`)); + await fs.writeFile( + path.resolve(sliceDir, 'reducers.ts'), + reducersTemplate + ) + console.log(chalk.green(`āœ… reducers.ts generated at ${sliceDir}`)) } // --- Generate types.ts --- const interfaceLines = Object.entries(initState).map(([key, value]) => { - const type = inferType(value); - return ` /** ${getTypeDescription(value)} */\n ${key}: ${type}`; - }); + const type = inferType(value) + return ` /** ${getTypeDescription(value)} */\n ${key}: ${type}` + }) const interfaceString = `export interface ${interfaceName} {\n${interfaceLines.join( - "\n" - )}\n}`; + '\n' + )}\n}` + + const typesPath = path.resolve(sliceDir, 'types.ts') - const typesPath = path.resolve(sliceDir, "types.ts"); - if (options.dryRun) { - console.log(chalk.yellow("šŸ” [DRY RUN] Would generate types.ts:")); - console.log(chalk.gray(interfaceString)); + console.log(chalk.yellow('šŸ” [DRY RUN] Would generate types.ts:')) + console.log(chalk.gray(interfaceString)) } else { - await fs.writeFile(typesPath, interfaceString); - console.log(chalk.green(`āœ… types.ts generated at ${typesPath}`)); + await fs.writeFile(typesPath, interfaceString) + console.log(chalk.green(`āœ… types.ts generated at ${typesPath}`)) } // --- Generate or update state.ts --- const stateTemplatePath = path.resolve( __dirname, - "../templates/state.ts.tpl" - ); - const stateTemplate = await fs.readFile(stateTemplatePath, "utf-8"); + '../templates/state.ts.tpl' + ) + const stateTemplate = await fs.readFile(stateTemplatePath, 'utf-8') + + // Determine the correct import path for the types file + // Calculate relative path from state.ts location to types.ts location + const typesFilePath = path.resolve(sliceDir, 'types.ts') + const stateDir = path.dirname(stateFilePath) + const relativePath = path.relative(stateDir, typesFilePath) + + // Convert to proper import path (use forward slashes and remove .ts extension) + const importPath = relativePath.replace(/\\/g, '/').replace(/\.ts$/, '') const renderedState = ejs.render(stateTemplate, { sliceName, - interfaceName - }); + interfaceName, + importPath: `./${importPath}` + }) - const stateExists = await fs.pathExists(stateFilePath); + const stateExists = await fs.pathExists(stateFilePath) if (!stateExists) { if (options.dryRun) { - console.log(chalk.yellow("šŸ” [DRY RUN] Would create state.ts:")); - console.log(chalk.gray(renderedState)); + console.log(chalk.yellow('šŸ” [DRY RUN] Would create state.ts:')) + console.log(chalk.gray(renderedState)) } else { - await fs.writeFile(stateFilePath, renderedState); - console.log(chalk.green(`āœ… state.ts created at ${stateFilePath}`)); + await fs.writeFile(stateFilePath, renderedState) + console.log(chalk.green(`āœ… state.ts created at ${stateFilePath}`)) } } else { if (options.verbose) { - console.log(chalk.yellow(`āš ļø state.ts already exists at ${stateFilePath}`)); - console.log(chalk.blue(`šŸ’” Consider manually adding: ${sliceName}: ${interfaceName}`)); + console.log( + chalk.yellow(`āš ļø state.ts already exists at ${stateFilePath}`) + ) + console.log( + chalk.blue( + `šŸ’” Consider manually adding: ${sliceName}: ${interfaceName}` + ) + ) } } // Summary if (options.verbose && !options.dryRun) { - console.log(chalk.cyan(`\nšŸ“Š Generated files summary:`)); - console.log(chalk.cyan(` • slice.ts - Redux slice with ${keys.length} actions`)); - console.log(chalk.cyan(` • reducers.ts - Reducer export`)); - console.log(chalk.cyan(` • types.ts - TypeScript interface`)); - console.log(chalk.cyan(` • state.ts - ${stateExists ? 'already exists' : 'created'}`)); + console.log(chalk.cyan('\nšŸ“Š Generated files summary:')) + console.log( + chalk.cyan(` • slice.ts - Redux slice with ${keys.length} actions`) + ) + console.log(chalk.cyan(' • reducers.ts - Reducer export')) + console.log(chalk.cyan(' • types.ts - TypeScript interface')) + console.log( + chalk.cyan( + ` • state.ts - ${stateExists ? 'already exists' : 'created'}` + ) + ) } - } catch (error) { if (error instanceof Error) { - throw new Error(`Failed to generate files: ${error.message}`); + throw new Error(`Failed to generate files: ${error.message}`) } - throw error; + throw error } } -function capitalize(str: string): string { - return str.charAt(0).toUpperCase() + str.slice(1); -} +// * Test added +export const capitalize = (str: string): string => + str.charAt(0).toUpperCase() + str.slice(1) -function inferType(value: any): string { - if (value === null) return "null"; - if (value === undefined) return "undefined"; - if (Array.isArray(value)) { - if (value.length === 0) return "any[]"; - // Try to infer array element type from first element - const firstElementType = inferType(value[0]); - return `${firstElementType}[]`; - } - if (typeof value === "object") { - // For objects, we could either use a generic Record type or create nested interfaces - // For now, let's use a more specific type - if (Object.keys(value).length === 0) return "Record"; - return "Record"; // Could be enhanced to generate nested interfaces +// export const inferType = (value: unknown): string => { +// if (value === null) return 'null' +// if (value === undefined) return 'undefined' +// if (Array.isArray(value)) { +// if (value.length === 0) return 'any[]' +// // Try to infer array element type from first element +// const firstElementType = inferType(value[0]) +// return `${firstElementType}[]` +// } +// if (typeof value === 'object') { +// // For objects, we could either use a generic Record type or create nested interfaces +// // For now, let's use a more specific type +// if (Object.keys(value).length === 0) return 'Record' +// return 'Record' // Could be enhanced to generate nested interfaces +// } +// return typeof value +// } + +// * Test added +export const inferType = (value: unknown): string => { + const seen = new WeakSet() + + const helper = (val: unknown): string => { + if (val === null) return 'null' + if (val === undefined) return 'undefined' + + // Detect special built-in types + if (val instanceof Date) return 'Date' + if (val instanceof RegExp) return 'RegExp' + if (val instanceof Map) { + // For Map, try to infer key and value types if possible + if (val.size === 0) return 'Map' + + const keyTypes = new Set() + const valueTypes = new Set() + val.forEach((v, k) => { + keyTypes.add(helper(k)) + valueTypes.add(helper(v)) + }) + + const keyType = + keyTypes.size === 1 + ? [...keyTypes][0] + : '(' + [...keyTypes].join(' | ') + ')' + const valueType = + valueTypes.size === 1 + ? [...valueTypes][0] + : '(' + [...valueTypes].join(' | ') + ')' + + return `Map<${keyType}, ${valueType}>` + } + if (val instanceof Set) { + if (val.size === 0) return 'Set' + + const elemTypes = new Set() + val.forEach(v => elemTypes.add(helper(v))) + const elemType = + elemTypes.size === 1 + ? [...elemTypes][0] + : '(' + [...elemTypes].join(' | ') + ')' + + return `Set<${elemType}>` + } + + const type = typeof val + + if (Array.isArray(val)) { + if (val.length === 0) return 'any[]' + + // Infer types of all elements (not just first) + const elementTypes = Array.from(new Set(val.map(item => helper(item)))) + const joinedTypes = + elementTypes.length === 1 + ? elementTypes[0] + : `(${elementTypes.join(' | ')})` + return `${joinedTypes}[]` + } + + if (type === 'object') { + if (seen.has(val)) return 'any /* circular */' + seen.add(val) + + const obj = val as Record + const keys = Object.keys(obj) + if (keys.length === 0) return 'Record' + + const props = keys.map(key => { + const propType = helper(obj[key]) + return `${key}: ${propType};` + }) + + return `{ ${props.join(' ')} }` + } + + if (type === 'function') return 'Function' + if (type === 'symbol') return 'symbol' + + return type } - return typeof value; + + return helper(value) } -function getTypeDescription(value: any): string { - if (value === null) return "Nullable value"; - if (value === undefined) return "Undefined value"; +// * Test added +export const getTypeDescription = (value: unknown): string => { + if (value === null) return 'Nullable value' + if (value === undefined) return 'Undefined value' if (Array.isArray(value)) { - return `Array with ${value.length} initial items`; + return `Array with ${value.length} initial items` } - if (typeof value === "object") { - return `Object with ${Object.keys(value).length} properties`; + if (typeof value === 'object') { + return `Object with ${Object.keys(value).length} properties` } - return `${typeof value} value`; + return `${typeof value} value` } diff --git a/src/index.ts b/src/index.ts index 93601b3..83dc2d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,36 +1,39 @@ #!/usr/bin/env node -import { Command } from 'commander' -import path from 'path' -import fs from 'fs-extra' -import chalk from 'chalk' -import { generateFilesFromInitState } from './generate' +import { Command } from 'commander' //CLI framework +import path from 'path' // Helps resolve full file paths +import fs from 'fs-extra' // Extends fs with more features +import chalk from 'chalk' // Colored console output +import { generateFilesFromInitState } from './generate.js' //Function to generate files from initState.ts +//Setup CLI Program const program = new Command() program .name('react-state-cli') .description('CLI to generate Redux boilerplate from initState.ts') - .version('1.0.1') + .version('1.1.0') +// Generate Command program .command('generate') .argument('', 'Path to initState.ts file') - .option('-v, --verbose', 'Enable verbose logging') - .option('--dry-run', 'Show what would be generated without creating files') + .option('-v, --verbose', 'Enable verbose logging') //Prints extra debug output + .option('--dry-run', 'Show what would be generated without creating files') // Simulates generation, doesn't actually write files + //Command Logic .action(async (initStatePath, options) => { try { const fullPath = path.resolve(process.cwd(), initStatePath) - - // Validate input file exists - if (!await fs.pathExists(fullPath)) { + + //? Validate input file exists + if (!(await fs.pathExists(fullPath))) { console.error(chalk.red(`āŒ Error: File not found: ${fullPath}`)) process.exit(1) } - // Validate it's a TypeScript file + //? Validate it's a TypeScript file if (!fullPath.endsWith('.ts') && !fullPath.endsWith('.js')) { - console.error(chalk.red(`āŒ Error: File must be a .ts or .js file`)) + console.error(chalk.red('āŒ Error: File must be a .ts or .js file')) process.exit(1) } @@ -38,12 +41,17 @@ program console.log(chalk.blue(`šŸ” Processing: ${fullPath}`)) } + //? Main task Execution await generateFilesFromInitState(fullPath, options) - - console.log(chalk.green(`šŸŽ‰ Redux boilerplate generated successfully!`)) - + + // * Success Message + console.log(chalk.green('šŸŽ‰ Redux boilerplate generated successfully!')) } catch (error) { - console.error(chalk.red(`āŒ Error: ${error instanceof Error ? error.message : 'Unknown error'}`)) + console.error( + chalk.red( + `āŒ Error: ${error instanceof Error ? error.message : 'Unknown error'}` + ) + ) if (options.verbose && error instanceof Error) { console.error(chalk.red(error.stack)) } @@ -51,4 +59,5 @@ program } }) +// ? Executes the CLI command program.parse() diff --git a/src/utils.ts b/src/utils.ts index 37bfeb0..b08ee17 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,10 +7,12 @@ import fs from 'fs-extra' * @returns Promise> - The parsed initial state object * @throws Error if file is invalid or doesn't export proper object */ -export async function parseInitState(filePath: string): Promise> { +export const parseInitState = async ( + filePath: string +): Promise> => { try { // Verify file exists - if (!await fs.pathExists(filePath)) { + if (!(await fs.pathExists(filePath))) { throw new Error(`File not found: ${filePath}`) } @@ -21,16 +23,27 @@ export async function parseInitState(filePath: string): Promise - } catch (error) { if (error instanceof Error) { // Re-throw with more context - throw new Error(`Failed to parse initial state from ${path.basename(filePath)}: ${error.message}`) + throw new Error( + `Failed to parse initial state from ${path.basename(filePath)}: ${error.message}` + ) } throw error } } /** - * Check if a string is a reserved JavaScript keyword + * Import a TypeScript file using ts-node */ -function isReservedKeyword(word: string): boolean { - const reservedWords = [ - 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', - 'delete', 'do', 'else', 'export', 'extends', 'finally', 'for', 'function', - 'if', 'import', 'in', 'instanceof', 'new', 'return', 'super', 'switch', - 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', - 'let', 'static', 'enum', 'implements', 'interface', 'package', 'private', - 'protected', 'public' - ] - return reservedWords.includes(word.toLowerCase()) +// * Testcase: added +export const importTypeScriptFile = async (filePath: string): Promise => { + try { + // Use dynamic import with ts-node/esm loader + const { register } = await import('ts-node') + + // Register ts-node for ESM + register({ + esm: true, + experimentalSpecifierResolution: 'node' + }) + + // Import the TypeScript file + const absolutePath = path.resolve(filePath) + const fileUrl = `file://${absolutePath}` + return await import(fileUrl) + } catch (error) { + // Fallback: try to compile and evaluate manually + const tsNode = await import('ts-node') + const service = tsNode.create({ + compilerOptions: { + module: 'ESNext', + target: 'ES2020', + esModuleInterop: true, + allowSyntheticDefaultImports: true + } + }) + + const fileContent = await fs.readFile(filePath, 'utf-8') + const compiled = service.compile(fileContent, filePath) + + // Create a temporary file to import + const tempFile = filePath.replace('.ts', '.temp.js') + await fs.writeFile(tempFile, compiled) + + try { + const absoluteTempPath = path.resolve(tempFile) + const result = await import(`file://${absoluteTempPath}`) + await fs.remove(tempFile) + return result + } catch (importError) { + await fs.remove(tempFile) + throw importError + } + } } /** - * Validate that a string is a valid TypeScript identifier + * Check if a string is a reserved JavaScript keyword */ -export function isValidIdentifier(name: string): boolean { - // Must start with letter, underscore, or dollar sign - // Can contain letters, digits, underscores, or dollar signs - const identifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/ - return identifierRegex.test(name) && !isReservedKeyword(name) +// * Testcase: added +export const isReservedKeyword = (word: string): boolean => { + const reservedWords = new Set([ + 'break', + 'case', + 'catch', + 'true', + 'false', + 'console', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'export', + 'extends', + 'finally', + 'for', + 'function', + 'if', + 'import', + 'in', + 'instanceof', + 'new', + 'return', + 'super', + 'switch', + 'this', + 'throw', + 'try', + 'typeof', + 'var', + 'null', + 'undefined', + 'void', + 'while', + 'with', + 'yield', + 'let', + 'static', + 'enum', + 'implements', + 'interface', + 'package', + 'private', + 'protected', + 'public', + 'NaN', + 'Infinity', + 'Date', + 'RegExp', + 'Map', + 'Set', + 'Symbol' + ]) + + return reservedWords.has(word) } - \ No newline at end of file diff --git a/templates/state.ts.tpl b/templates/state.ts.tpl index 4f40461..0437732 100644 --- a/templates/state.ts.tpl +++ b/templates/state.ts.tpl @@ -1,4 +1,4 @@ -import { <%= interfaceName %> } from './<%= sliceName %>/types' +import { <%= interfaceName %> } from '<%= importPath %>' export interface RootState { <%= sliceName %>: <%= interfaceName %> diff --git a/test/generate.test.ts b/test/generate.test.ts new file mode 100644 index 0000000..fb57e95 --- /dev/null +++ b/test/generate.test.ts @@ -0,0 +1,297 @@ +import { describe, expect, it } from 'vitest' +import { + capitalize, + getTypeDescription, + inferType +} from '../src/generate' + + +describe('capitalize function', () => { + describe('Real-world scenarios', () => { + it('handles camelCase variable names', () => { + expect(capitalize('userName')).toBe('UserName') + expect(capitalize('firstName')).toBe('FirstName') + expect(capitalize('isActive')).toBe('IsActive') + }) + + it('handles snake_case variable names', () => { + expect(capitalize('user_name')).toBe('User_name') + expect(capitalize('first_name')).toBe('First_name') + }) + + it('handles kebab-case variable names', () => { + expect(capitalize('user-name')).toBe('User-name') + expect(capitalize('first-name')).toBe('First-name') + }) + + it('handles PascalCase variable names', () => { + expect(capitalize('UserName')).toBe('UserName') + expect(capitalize('FirstName')).toBe('FirstName') + }) + + it('handles typical Redux slice names', () => { + expect(capitalize('auth')).toBe('Auth') + expect(capitalize('user')).toBe('User') + expect(capitalize('shoppingCart')).toBe('ShoppingCart') + expect(capitalize('userProfile')).toBe('UserProfile') + }) + }) + + describe('Edge cases - Long strings', () => { + it('handles very long string', () => { + const longString = 'a'.repeat(1000) + const expected = 'A' + 'a'.repeat(999) + expect(capitalize(longString)).toBe(expected) + }) + + it('handles long string starting with number', () => { + const longString = '1' + 'a'.repeat(999) + expect(capitalize(longString)).toBe(longString) + }) + }) + + describe('Edge cases - Complex scenarios', () => { + it('handles string with multiple words', () => { + expect(capitalize('hello world')).toBe('Hello world') + }) + + it('handles string with dots', () => { + expect(capitalize('config.json')).toBe('Config.json') + }) + + it('handles string with file extensions', () => { + expect(capitalize('index.ts')).toBe('Index.ts') + expect(capitalize('component.tsx')).toBe('Component.tsx') + }) + + it('handles string with version numbers', () => { + expect(capitalize('v1.2.3')).toBe('V1.2.3') + }) + + it('handles string with common abbreviations', () => { + expect(capitalize('api')).toBe('Api') + expect(capitalize('ui')).toBe('Ui') + expect(capitalize('db')).toBe('Db') + }) + }) + + describe('Edge cases - Boundary conditions', () => { + it('handles string with only one character that is not a letter', () => { + expect(capitalize('1')).toBe('1') + expect(capitalize('!')).toBe('!') + expect(capitalize(' ')).toBe(' ') + }) + + it('handles string with alternating case', () => { + expect(capitalize('aAbBcC')).toBe('AAbBcC') + }) + + it('handles string with all uppercase', () => { + expect(capitalize('HELLO')).toBe('HELLO') + }) + + it('handles string with all lowercase', () => { + expect(capitalize('hello')).toBe('Hello') + }) + }) +}) + +describe('inferType function', () => { + describe('Real-world scenarios', () => { + it('should infer primitive types', () => { + expect(inferType(1)).toBe('number') + expect(inferType('hello')).toBe('string') + expect(inferType(true)).toBe('boolean') + expect(inferType(null)).toBe('null') + expect(inferType(undefined)).toBe('undefined') + }) + + it('should infer array types', () => { + expect(inferType([])).toBe('any[]') + expect(inferType([1, 2, 3])).toBe('number[]') + expect(inferType(['a', 'b'])).toBe('string[]') + expect(inferType([1, 'a'])).toBe('(number | string)[]') + }) + + it('should infer object types', () => { + expect(inferType({})).toBe('Record') + expect(inferType({ name: 'John', age: 30 })).toBe( + '{ name: string; age: number; }' + ) + }) + }) + + describe('Edge cases - Boundary conditions', () => { + it('handles strings that may look like numbers', () => { + expect(inferType('1')).toBe('string') // Clarified: still a string + expect(inferType('001')).toBe('string') + expect(inferType(' ')).toBe('string') + expect(inferType('!')).toBe('string') + }) + + it('handles special numeric values', () => { + expect(inferType(NaN)).toBe('number') + expect(inferType(Infinity)).toBe('number') + expect(inferType(-Infinity)).toBe('number') + }) + + it('handles Date and RegExp', () => { + expect(inferType(new Date())).toBe('Date') + expect(inferType(/abc/)).toBe('RegExp') + }) + + it('handles functions', () => { + expect(inferType(() => {})).toBe('Function') + expect(inferType(function namedFunc() {})).toBe('Function') + }) + }) + + describe('Edge cases - Complex scenarios', () => { + const complexObject = { + interface: 'John', + age: 30, + class: true, + address: { + street: '123 Main St', + city: 'Anytown', + state: 'CA', + zip: '12345' + }, + phone: '+1234567890', + null: 'john@example.com', + website: 'https://www.john.com', + skills: ['JavaScript', 'React', 'Node.js'], + projects: [ + { null: 'Project 1', Number: 'A project about JavaScript' }, + { name: 'Project 2', description: undefined }, + { NaN: 'Project 3', description: null } + ], + hobbies: ['reading', 'traveling', 'photography'] + } + + it('infers complex nested object types', () => { + const inferredType = inferType(complexObject) + expect(inferredType).toContain('address: {') // Nested object check + expect(inferredType).toContain('skills: string[]') + expect(inferredType).toMatch(/projects:\s*\(.+\)\[\]/) + }) + }) + + describe('Miscellaneous', () => { + it('infers Map, Set, and Symbol types', () => { + expect(inferType(new Map())).toBe('Map') + expect(inferType(new Set([1, 2, 3]))).toBe('Set') + expect(inferType(Symbol('sym'))).toBe('symbol') + }) + + it('handles deeply nested structures', () => { + const deep = { a: { b: { c: { d: [1, 2, 3] } } } } + expect(inferType(deep)).toBe('{ a: { b: { c: { d: number[]; }; }; }; }') + }) + + it('handles circular references gracefully', () => { + const obj: any = {} + obj.self = obj + + // Expect inferType to either throw a warning, return a placeholder, or not crash + expect(() => inferType(obj)).not.toThrow() + }) + }) +}) + +describe('getTypeDescription function - Boundary Tests', () => { + it('should describe null value', () => { + expect(getTypeDescription(null)).toBe('Nullable value') + }) + + it('should describe undefined value', () => { + expect(getTypeDescription(undefined)).toBe('Undefined value') + }) + it('should describe undefined value', () => { + expect(getTypeDescription('-')).toBe('string value') + }) + + it('should describe empty array', () => { + expect(getTypeDescription([])).toBe('Array with 0 initial items') + }) + + it('should describe non-empty array', () => { + expect(getTypeDescription([1, 2, 3])).toBe('Array with 3 initial items') + }) + + it('should describe empty object', () => { + expect(getTypeDescription({})).toBe('Object with 0 properties') + }) + + it('should describe non-empty object', () => { + expect(getTypeDescription({ a: 1, b: 'hello' })).toBe( + 'Object with 2 properties' + ) + }) + + it('should describe number value', () => { + expect(getTypeDescription(42)).toBe('number value') + }) + + it('should describe string value', () => { + expect(getTypeDescription('test')).toBe('string value') + }) + + it('should describe boolean value', () => { + expect(getTypeDescription(true)).toBe('boolean value') + }) + + it('should describe function value', () => { + expect(getTypeDescription(() => {})).toBe('function value') + }) + + it('should describe symbol value', () => { + expect(getTypeDescription(Symbol('sym'))).toBe('symbol value') + }) + + it('should describe object created from class instance', () => { + class MyClass { + prop = 123 + } + expect(getTypeDescription(new MyClass())).toBe('Object with 1 properties') + }) +}) + +// const tmpDir = path.join(os.tmpdir(), 'gen-test') + +// async function writeInitState(name: string, content: string): Promise { +// const dir = path.join(tmpDir, 'src', 'redux', name) +// await fs.ensureDir(dir) +// const filePath = path.join(dir, 'initState.ts') +// await fs.writeFile(filePath, content) +// return filePath +// } + +// describe('generateFilesFromInitState', () => { +// beforeEach(async () => { +// await fs.emptyDir(tmpDir) +// }) + +// it('should generate slice, types, reducers, and state files', async () => { +// const initStatePath = await writeInitState( +// 'user', +// `export default { +// name: "Alice", +// age: 25 +// }` +// ) + +// await generateFilesFromInitState(initStatePath) + +// // Check that files exist +// const expectedFiles = ['slice.ts', 'types.ts', 'reducers.ts'] +// for (const file of expectedFiles) { +// const fullPath = path.join(tmpDir, 'src', 'redux', 'user', file) +// const exists = await fs.pathExists(fullPath) +// expect(exists).toBe(true) +// } + +// const stateFile = path.join(tmpDir, 'src', 'redux', 'state.ts') +// expect(await fs.pathExists(stateFile)).toBe(true) +// }) +// }) diff --git a/test/generateFilesFromInitState.test.ts b/test/generateFilesFromInitState.test.ts new file mode 100644 index 0000000..7c1302b --- /dev/null +++ b/test/generateFilesFromInitState.test.ts @@ -0,0 +1,133 @@ +import path from 'path' +import fs from 'fs-extra' +import { expect } from 'chai' +import { generateFilesFromInitState } from '../src/generate' +import { beforeEach, describe, it } from 'vitest' + +const tmpDir = path.resolve(__dirname, '..', '__temp__') + +async function writeInitState( + sliceName: string, + content: string +): Promise { + const sliceDir = path.join(tmpDir, 'src', 'redux', sliceName) + const filePath = path.join(sliceDir, 'initState.ts') + await fs.ensureDir(sliceDir) + await fs.writeFile(filePath, content) + return filePath +} + +describe('generateFilesFromInitState', () => { + beforeEach(async () => { + await fs.emptyDir(tmpDir) + }) + + it('should generate slice, types, reducers, and state files', async () => { + const initStatePath = await writeInitState( + 'user', + 'export default { name: \'Alice\', age: 25 }' + ) + await generateFilesFromInitState(initStatePath) + + const expectedFiles = ['slice.ts', 'types.ts', 'reducers.ts'] + for (const file of expectedFiles) { + const filePath = path.join(tmpDir, 'src', 'redux', 'user', file) + const exists = await fs.pathExists(filePath) + expect(exists).to.be.true + } + + const statePath = path.join(tmpDir, 'src', 'redux', 'state.ts') + expect(await fs.pathExists(statePath)).to.be.true + }) + + it('should not write any files in dryRun mode', async () => { + const initStatePath = await writeInitState( + 'user', + 'export default { x: 1 }' + ) + await generateFilesFromInitState(initStatePath, { dryRun: true }) + + const generatedFiles = ['slice.ts', 'types.ts', 'reducers.ts', 'state.ts'] + for (const file of generatedFiles) { + const fullPath = path.join(tmpDir, 'src', 'redux', 'user', file) + expect(await fs.pathExists(fullPath)).to.be.false + } + + const stateFile = path.join(tmpDir, 'src', 'redux', 'state.ts') + expect(await fs.pathExists(stateFile)).to.be.false + }) + + it('should skip state.ts if it already exists', async () => { + const initStatePath = await writeInitState( + 'user', + 'export default { test: true }' + ) + const statePath = path.join(tmpDir, 'src', 'redux', 'state.ts') + + await fs.ensureFile(statePath) + await fs.writeFile(statePath, '// existing state') + + await generateFilesFromInitState(initStatePath) + + const content = await fs.readFile(statePath, 'utf-8') + expect(content).to.equal('// existing state') + }) + + it('should throw if initState object is empty', async () => { + const initStatePath = await writeInitState('User', 'export default {}') + + try { + await generateFilesFromInitState(initStatePath) + throw new Error('Expected error was not thrown') + } catch (err: any) { + expect(err.message).to.include('Initial state object is empty') + } + }) + + it('should handle large state object', async () => { + const largeState = Array.from({ length: 100 }) + .map((_, i) => `key${i}: ${i}`) + .join(',\n') + const initStatePath = await writeInitState( + 'big', + `export default {\n${largeState}\n}` + ) + + await generateFilesFromInitState(initStatePath) + + const typesFile = path.join(tmpDir, 'src', 'redux', 'big', 'types.ts') + const content = await fs.readFile(typesFile, 'utf-8') + expect(content).to.include('key99') + }) + + it('should throw if file does not exist', async () => { + const nonExistentPath = path.join( + tmpDir, + 'src', + 'redux', + 'missing', + 'initState.ts' + ) + + try { + await generateFilesFromInitState(nonExistentPath) + throw new Error('Expected error was not thrown') + } catch (err: any) { + expect(err.message).to.include('Failed to generate files') + } + }) + + it('should throw if file contains invalid JavaScript', async () => { + const initStatePath = await writeInitState( + 'broken', + 'export default { name:' + ) + + try { + await generateFilesFromInitState(initStatePath) + throw new Error('Expected error was not thrown') + } catch (err: any) { + expect(err.message).to.include('Failed to generate files') + } + }) +}) diff --git a/test/utils.test.ts b/test/utils.test.ts new file mode 100644 index 0000000..9f9c012 --- /dev/null +++ b/test/utils.test.ts @@ -0,0 +1,368 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { + importTypeScriptFile, + isReservedKeyword, + parseInitState +} from '../src/utils' + +import fs from 'fs-extra' +import path from 'path' +import os from 'os' + +describe('isReservedKeyword function', () => { + describe('Real-world scenarios', () => { + it('should return true for reserved JavaScript keywords', () => { + expect(isReservedKeyword('for')).toBe(true) + expect(isReservedKeyword('while')).toBe(true) + expect(isReservedKeyword('if')).toBe(true) + expect(isReservedKeyword('else')).toBe(true) + expect(isReservedKeyword('return')).toBe(true) + expect(isReservedKeyword('class')).toBe(true) + expect(isReservedKeyword('const')).toBe(true) + expect(isReservedKeyword('let')).toBe(true) + expect(isReservedKeyword('var')).toBe(true) + expect(isReservedKeyword('function')).toBe(true) + expect(isReservedKeyword('try')).toBe(true) + expect(isReservedKeyword('catch')).toBe(true) + expect(isReservedKeyword('new')).toBe(true) + expect(isReservedKeyword('null')).toBe(true) + expect(isReservedKeyword('true')).toBe(true) + expect(isReservedKeyword('false')).toBe(true) + }) + + it('should return false for non-keywords and identifiers', () => { + expect(isReservedKeyword('hello')).toBe(false) + expect(isReservedKeyword('myVar')).toBe(false) + expect(isReservedKeyword('returnValue')).toBe(false) + expect(isReservedKeyword('functionName')).toBe(false) + expect(isReservedKeyword('letmein')).toBe(false) + }) + + it('should handle case sensitivity correctly', () => { + expect(isReservedKeyword('Function')).toBe(false) + expect(isReservedKeyword('Class')).toBe(false) + expect(isReservedKeyword('Var')).toBe(false) + }) + + it('should handle edge cases', () => { + expect(isReservedKeyword('')).toBe(false) + expect(isReservedKeyword('123')).toBe(false) + expect(isReservedKeyword('$for')).toBe(false) + expect(isReservedKeyword('while1')).toBe(false) + expect(isReservedKeyword('_var')).toBe(false) + }) + }) +}) + +describe('importTypeScriptFile function', () => { + const tmpDir = path.join(os.tmpdir(), 'ts-import-test') + + beforeAll(async () => { + await fs.ensureDir(tmpDir) + }) + + afterAll(async () => { + await fs.remove(tmpDir) + }) + + const createTempTSFile = async ( + name: string, + content: string + ): Promise => { + const filePath = path.join(tmpDir, name) + await fs.writeFile(filePath, content) + return filePath + } + + describe('Real-world usage', () => { + it('should import a valid TypeScript module with default export', async () => { + const filePath = await createTempTSFile( + 'defaultExport.ts', + ` + const data = { message: 'hello world' } + export default data + ` + ) + const module = await importTypeScriptFile(filePath) + expect(module.default.message).toBe('hello world') + }) + + it('should import named exports from a TypeScript file', async () => { + const filePath = await createTempTSFile( + 'namedExport.ts', + ` + export const value = 42 + ` + ) + const module = await importTypeScriptFile(filePath) + expect(module.value).toBe(42) + }) + + it('should import mixed exports (default + named)', async () => { + const filePath = await createTempTSFile( + 'mixedExport.ts', + ` + const config = { env: 'test' } + export default config + export const version = '1.0.0' + ` + ) + const module = await importTypeScriptFile(filePath) + expect(module.default.env).toBe('test') + expect(module.version).toBe('1.0.0') + }) + }) + + describe('Edge cases', () => { + it('should throw if the file has syntax error', async () => { + const filePath = await createTempTSFile( + 'badSyntax.ts', + ` + export const x == 5 + ` + ) + await expect(importTypeScriptFile(filePath)).rejects.toThrow() + }) + + it('should throw if the file does not exist', async () => { + const filePath = path.join(tmpDir, 'nonexistent.ts') + await expect(importTypeScriptFile(filePath)).rejects.toThrow() + }) + + it('should handle file with no exports', async () => { + const filePath = await createTempTSFile( + 'noExports.ts', + ` + const secret = 'internal only' + ` + ) + const module = await importTypeScriptFile(filePath) + expect(Object.keys(module)).toEqual([]) + }) + }) + describe('Boundary and unusual conditions', () => { + it('should work with a large file with many exports', async () => { + const manyExports = Array.from( + { length: 100 }, + (_, i) => `export const val${i} = ${i}` + ).join('\n') + const filePath = await createTempTSFile('manyExports.ts', manyExports) + const module = await importTypeScriptFile(filePath) + expect(module.val0).toBe(0) + expect(module.val99).toBe(99) + }) + + it('should support exporting functions and classes', async () => { + const filePath = await createTempTSFile( + 'functionsAndClasses.ts', + ` + export function greet(name: string) { + return 'Hello, ' + name + } + export class Greeter { + greet(name: string) { + return 'Hi, ' + name + } + } + ` + ) + const module = await importTypeScriptFile(filePath) + expect(module.greet('Alice')).toBe('Hello, Alice') + const greeter = new module.Greeter() + expect(greeter.greet('Bob')).toBe('Hi, Bob') + }) + }) +}) + +describe('parseInitState function', () => { + const tmpDir = path.join(os.tmpdir(), 'parse-init-state-test') + + beforeAll(async () => { + await fs.ensureDir(tmpDir) + }) + + afterAll(async () => { + await fs.remove(tmpDir) + }) + + const createTempFile = async ( + name: string, + content: string + ): Promise => { + const filePath = path.join(tmpDir, name) + await fs.writeFile(filePath, content) + return filePath + } + + describe('Real-world usage', () => { + it('should parse a valid JS file with default object export', async () => { + const filePath = await createTempFile( + 'validExport.js', + ` + export default { + foo: 'bar', + baz: 123 + } + ` + ) + const result = await parseInitState(filePath) + expect(result).toEqual({ foo: 'bar', baz: 123 }) + }) + + it('should throw if file does not exist', async () => { + const filePath = path.join(tmpDir, 'nonexistent.js') + await expect(parseInitState(filePath)).rejects.toThrow(/File not found/) + }) + + it('should throw if path is not a file', async () => { + const dirPath = path.join(tmpDir, 'aDirectory') + await fs.ensureDir(dirPath) + await expect(parseInitState(dirPath)).rejects.toThrow(/not a file/) + }) + }) + + describe('Validation of exports', () => { + it('should throw if default export is missing', async () => { + const filePath = await createTempFile( + 'noDefaultExport.js', + ` + export const foo = 1 + ` + ) + await expect(parseInitState(filePath)).rejects.toThrow(/default object/) + }) + + it('should throw if default export is not an object', async () => { + const filePath = await createTempFile( + 'defaultIsNotObject.js', + ` + export default 42 + ` + ) + await expect(parseInitState(filePath)).rejects.toThrow( + /must be an object/ + ) + }) + + it('should throw if default export is an array', async () => { + const filePath = await createTempFile( + 'defaultIsArray.js', + ` + export default [1, 2, 3] + ` + ) + await expect(parseInitState(filePath)).rejects.toThrow(/not an array/) + }) + }) + + describe('Object structure validation', () => { + it('should throw if property key is empty string or not a string', async () => { + const filePath = await createTempFile( + 'invalidKey.js', + ` + export default { + '': 'empty', + 123: 'number key' + } + ` + ) + // Because JS keys are always strings, the number key is coerced, so we only test empty string key. + await expect(parseInitState(filePath)).rejects.toThrow( + /Invalid property key/ + ) + }) + + it('should throw if property key is a reserved JavaScript keyword', async () => { + // Assuming isReservedKeyword('for') === true + const filePath = await createTempFile( + 'reservedKeyword.js', + ` + export default { + for: 'loop' + } + ` + ) + await expect(parseInitState(filePath)).rejects.toThrow( + /reserved JavaScript keyword/ + ) + }) + + it('should pass if all keys are valid and non-reserved', async () => { + const filePath = await createTempFile( + 'validKeys.js', + ` + export default { + name: 'ChatGPT', + age: 4, + _private: true + } + ` + ) + const result = await parseInitState(filePath) + expect(result).toEqual({ + name: 'ChatGPT', + age: 4, + _private: true + }) + }) + }) + + describe('Handling TypeScript files', () => { + it('should parse a valid TypeScript file with default export', async () => { + const filePath = await createTempFile( + 'validTS.ts', + ` + const state = { ready: true } + export default state + ` + ) + const result = await parseInitState(filePath) + expect(result).toEqual({ ready: true }) + }) + }) + describe('Empty and Half State Cases', () => { + it('should accept an empty object as default export', async () => { + const filePath = await createTempFile( + 'emptyState.js', + ` + export default {} + ` + ) + const result = await parseInitState(filePath) + expect(result).toEqual({}) + }) + + it('should accept an object with keys having null or undefined values (half state)', async () => { + const filePath = await createTempFile( + 'halfState.js', + ` + export default { + foo: null, + bar: undefined, + baz: 'valid' + } + ` + ) + const result = await parseInitState(filePath) + expect(result).toEqual({ + foo: null, + bar: undefined, + baz: 'valid' + }) + }) + + it('should throw if object has keys that are empty strings', async () => { + const filePath = await createTempFile( + 'emptyKey.js', + ` + export default { + "": 'empty key' + } + ` + ) + await expect(parseInitState(filePath)).rejects.toThrow( + /Invalid property key/ + ) + }) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index ae698dd..2040472 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ /* Basic Options */ "target": "es2020", "lib": ["es2020"], - "module": "commonjs", + "module": "ESNext", "outDir": "./dist", "rootDir": "./src", "strict": true, diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..f7d0b93 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + globals: true, // so you can use `describe`, `it`, `expect` globally like Jest + environment: 'node' + } +})