From 95be8850543d399318f16d220b4d7180615e9ca6 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 16:59:42 -0400 Subject: [PATCH 01/18] Add frameworks/angular package skeleton --- frameworks/angular/CHANGELOG.md | 7 ++ frameworks/angular/eslint.config.ts | 28 +++++ frameworks/angular/package.json | 71 +++++++++++++ frameworks/angular/src/components/index.ts | 1 + frameworks/angular/src/functions/index.ts | 1 + frameworks/angular/src/index.ts | 3 + frameworks/angular/src/types/index.ts | 1 + frameworks/angular/src/vitest/setup.ts | 6 ++ frameworks/angular/tsconfig.json | 18 ++++ frameworks/angular/tsdown.config.ts | 13 +++ frameworks/angular/vitest.config.ts | 18 ++++ pnpm-lock.yaml | 113 ++++++++++++++++++++- 12 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 frameworks/angular/CHANGELOG.md create mode 100644 frameworks/angular/eslint.config.ts create mode 100644 frameworks/angular/package.json create mode 100644 frameworks/angular/src/components/index.ts create mode 100644 frameworks/angular/src/functions/index.ts create mode 100644 frameworks/angular/src/index.ts create mode 100644 frameworks/angular/src/types/index.ts create mode 100644 frameworks/angular/src/vitest/setup.ts create mode 100644 frameworks/angular/tsconfig.json create mode 100644 frameworks/angular/tsdown.config.ts create mode 100644 frameworks/angular/vitest.config.ts diff --git a/frameworks/angular/CHANGELOG.md b/frameworks/angular/CHANGELOG.md new file mode 100644 index 00000000..6b8c75ed --- /dev/null +++ b/frameworks/angular/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable changes to the library will be documented in this file. + +## v0.0.1 (Month DD, YYYY) + +- Initial release diff --git a/frameworks/angular/eslint.config.ts b/frameworks/angular/eslint.config.ts new file mode 100644 index 00000000..497e3216 --- /dev/null +++ b/frameworks/angular/eslint.config.ts @@ -0,0 +1,28 @@ +import eslint from '@eslint/js'; +import { commonRules, importPlugin, jsdoc } from '@formisch/eslint-config'; +import pluginSecurity from 'eslint-plugin-security'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['eslint.config.ts', 'dist', 'coverage'] }, + eslint.configs.recommended, + jsdoc.configs['flat/recommended'], + pluginSecurity.configs.recommended, + ...tseslint.configs.strictTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + { + name: 'app/files-to-lint', + files: ['src/**/*.ts'], + extends: [importPlugin.flatConfigs.recommended], + plugins: { jsdoc }, + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + ...commonRules, + }, + } +); diff --git a/frameworks/angular/package.json b/frameworks/angular/package.json new file mode 100644 index 00000000..94a31286 --- /dev/null +++ b/frameworks/angular/package.json @@ -0,0 +1,71 @@ +{ + "name": "@formisch/angular", + "description": "The lightweight, schema-first, and fully type-safe form library for Angular", + "version": "0.0.1", + "license": "MIT", + "author": "Fabian Hiller", + "homepage": "https://formisch.dev", + "repository": { + "type": "git", + "url": "https://github.com/open-circle/formisch" + }, + "keywords": [ + "angular", + "form", + "typescript", + "schema", + "validation" + ], + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "sideEffects": false, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown", + "test": "vitest run --typecheck --passWithNoTests", + "lint": "eslint \"src/**/*.ts*\" && tsc --noEmit", + "lint.fix": "eslint \"src/**/*.ts*\" --fix", + "format": "prettier --write ./src", + "format.check": "prettier --check ./src" + }, + "devDependencies": { + "@angular/common": "^19.0.0", + "@angular/compiler": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/platform-browser": "^19.0.0", + "@formisch/core": "workspace:*", + "@formisch/eslint-config": "workspace:*", + "@formisch/methods": "workspace:*", + "@types/node": "^24.0.0", + "@vitest/coverage-v8": "^3.2.4", + "eslint": "^9.31.0", + "jsdom": "^26.1.0", + "tsdown": "^0.16.8", + "typescript": "~5.8.3", + "valibot": "^1.2.0", + "vitest": "^3.2.4", + "zone.js": "^0.15.0" + }, + "peerDependencies": { + "@angular/core": ">=17.0.0", + "typescript": ">=5", + "valibot": "^1.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } +} diff --git a/frameworks/angular/src/components/index.ts b/frameworks/angular/src/components/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/frameworks/angular/src/components/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/frameworks/angular/src/functions/index.ts b/frameworks/angular/src/functions/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/frameworks/angular/src/functions/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/frameworks/angular/src/index.ts b/frameworks/angular/src/index.ts new file mode 100644 index 00000000..bf9d0a99 --- /dev/null +++ b/frameworks/angular/src/index.ts @@ -0,0 +1,3 @@ +export * from './components/index.ts'; +export * from './functions/index.ts'; +export * from './types/index.ts'; diff --git a/frameworks/angular/src/types/index.ts b/frameworks/angular/src/types/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/frameworks/angular/src/types/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/frameworks/angular/src/vitest/setup.ts b/frameworks/angular/src/vitest/setup.ts new file mode 100644 index 00000000..46b6f57c --- /dev/null +++ b/frameworks/angular/src/vitest/setup.ts @@ -0,0 +1,6 @@ +import { TestBed } from '@angular/core/testing'; +import { afterEach } from 'vitest'; + +afterEach(() => { + TestBed.resetTestingModule(); +}); diff --git a/frameworks/angular/tsconfig.json b/frameworks/angular/tsconfig.json new file mode 100644 index 00000000..d430093f --- /dev/null +++ b/frameworks/angular/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "allowImportingTsExtensions": true, + "declaration": true, + "experimentalDecorators": true, + "isolatedDeclarations": true, + "exactOptionalPropertyTypes": true, + "lib": ["ES2020", "DOM"], + "module": "ES2020", + "moduleResolution": "Bundler", + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "target": "ES2020", + "types": ["node"] + }, + "include": ["src"] +} diff --git a/frameworks/angular/tsdown.config.ts b/frameworks/angular/tsdown.config.ts new file mode 100644 index 00000000..0d0c7a17 --- /dev/null +++ b/frameworks/angular/tsdown.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + entry: ['./src/index.ts'], + outDir: 'dist', + outExtensions: () => ({ + js: '.js', + dts: '.d.ts', + }), + dts: { + resolve: ['@formisch/core/angular', '@formisch/methods/angular'], + }, +}); diff --git a/frameworks/angular/vitest.config.ts b/frameworks/angular/vitest.config.ts new file mode 100644 index 00000000..e5457132 --- /dev/null +++ b/frameworks/angular/vitest.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'jsdom', + setupFiles: ['./src/vitest/setup.ts'], + coverage: { + include: ['src'], + exclude: [ + 'src/types', + 'src/vitest', + '**/index.ts', + '**/*.test.ts', + '**/*.test-d.ts', + ], + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0df9efc..accceade 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,57 @@ importers: specifier: ^5.8.3 version: 5.8.3 + frameworks/angular: + devDependencies: + '@angular/common': + specifier: ^19.0.0 + version: 19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^19.0.0 + version: 19.2.22 + '@angular/core': + specifier: ^19.0.0 + version: 19.2.22(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': + specifier: ^19.0.0 + version: 19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1)) + '@formisch/core': + specifier: workspace:* + version: link:../../packages/core + '@formisch/eslint-config': + specifier: workspace:* + version: link:../../packages/eslint-config + '@formisch/methods': + specifier: workspace:* + version: link:../../packages/methods + '@types/node': + specifier: ^24.0.0 + version: 24.10.1 + '@vitest/coverage-v8': + specifier: ^3.2.4 + version: 3.2.4(vitest@3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + eslint: + specifier: ^9.31.0 + version: 9.39.1(jiti@2.6.1) + jsdom: + specifier: ^26.1.0 + version: 26.1.0 + tsdown: + specifier: ^0.16.8 + version: 0.16.8(publint@0.3.15)(typescript@5.8.3)(vue-tsc@3.1.2(typescript@5.8.3)) + typescript: + specifier: ~5.8.3 + version: 5.8.3 + valibot: + specifier: ^1.2.0 + version: 1.2.0(typescript@5.8.3) + vitest: + specifier: ^3.2.4 + version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + zone.js: + specifier: ^0.15.0 + version: 0.15.1 + frameworks/preact: devDependencies: '@formisch/core': @@ -397,7 +448,7 @@ importers: devDependencies: '@angular/core': specifier: ^21.0.0 - version: 21.2.13(rxjs@7.8.2) + version: 21.2.13(rxjs@7.8.2)(zone.js@0.15.1) '@formisch/eslint-config': specifier: workspace:* version: link:../eslint-config @@ -992,6 +1043,24 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@angular/common@19.2.22': + resolution: {integrity: sha512-OGvJkK9xvlXF0LsM8T2NTgL6ehr+P4t493xztdIwiO7CzseDzHCBNZ8L1TwaVqqP3Xj0UhGTtWXp7YN4N9+u7Q==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/core': 19.2.22 + rxjs: ^6.5.3 || ^7.4.0 + + '@angular/compiler@19.2.22': + resolution: {integrity: sha512-NO3q39TMq88MRyBM9E/cSn4KFEItPrp79izX3nRkWlGQ0D9nJPdzm2cHtZoB9dCZ3N6EOazTgBK+2jKylQrUFQ==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + + '@angular/core@19.2.22': + resolution: {integrity: sha512-ImujBPIrV9eetYl2GaEBRw9CetklFtXB11oVaxOjflE2z6juTjugzXhszo/khMLj6UFfS+Anvp7Vv/Ot6DJL7A==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + rxjs: ^6.5.3 || ^7.4.0 + zone.js: ~0.15.0 + '@angular/core@21.2.13': resolution: {integrity: sha512-23tS4oNL8nvkHcI4l9rbruQs2WS4yqQmBVQxWakqS9cmRpArLGgveR+hKNU5tPXm5EAi8oLO34/Zy7z70jUpCg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -1005,6 +1074,17 @@ packages: zone.js: optional: true + '@angular/platform-browser@19.2.22': + resolution: {integrity: sha512-7OE80uM3DYDXa8D+1c3D231yzfqWz+2YaQElKK5MTPIDobSA9Dsu4YIFX80FRFxaopo3673RepZgxxFx3KzRzA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/animations': 19.2.22 + '@angular/common': 19.2.22 + '@angular/core': 19.2.22 + peerDependenciesMeta: + '@angular/animations': + optional: true + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -10475,6 +10555,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zone.js@0.15.1: + resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -10490,11 +10573,35 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular/core@21.2.13(rxjs@7.8.2)': + '@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: + '@angular/core': 19.2.22(rxjs@7.8.2)(zone.js@0.15.1) rxjs: 7.8.2 tslib: 2.8.1 + '@angular/compiler@19.2.22': + dependencies: + tslib: 2.8.1 + + '@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1)': + dependencies: + rxjs: 7.8.2 + tslib: 2.8.1 + zone.js: 0.15.1 + + '@angular/core@21.2.13(rxjs@7.8.2)(zone.js@0.15.1)': + dependencies: + rxjs: 7.8.2 + tslib: 2.8.1 + optionalDependencies: + zone.js: 0.15.1 + + '@angular/platform-browser@19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))': + dependencies: + '@angular/common': 19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.22(rxjs@7.8.2)(zone.js@0.15.1) + tslib: 2.8.1 + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -22286,4 +22393,6 @@ snapshots: zod@3.25.76: {} + zone.js@0.15.1: {} + zwitch@2.0.4: {} From 323204e465030548ca08d5948fd6f312233698d5 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 17:38:45 -0400 Subject: [PATCH 02/18] Add angular build target to @formisch/methods --- packages/methods/package.json | 4 ++++ packages/methods/tsdown.config.ts | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/methods/package.json b/packages/methods/package.json index 12bd3e9a..ec8bd878 100644 --- a/packages/methods/package.json +++ b/packages/methods/package.json @@ -20,6 +20,10 @@ ], "type": "module", "exports": { + "./angular": { + "types": "./dist/index.angular.d.ts", + "default": "./dist/index.angular.js" + }, "./preact": { "types": "./dist/index.preact.d.ts", "default": "./dist/index.preact.js" diff --git a/packages/methods/tsdown.config.ts b/packages/methods/tsdown.config.ts index 864907a3..0367792d 100644 --- a/packages/methods/tsdown.config.ts +++ b/packages/methods/tsdown.config.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import type { RolldownPluginOption } from 'rolldown'; import { defineConfig, type UserConfig, type UserConfigFn } from 'tsdown'; -type Framework = 'preact' | 'qwik' | 'react' | 'solid' | 'svelte' | 'vue'; +type Framework = 'angular' | 'preact' | 'qwik' | 'react' | 'solid' | 'svelte' | 'vue'; /** * Rolldown plugin to rewrite framework-specific imports. @@ -89,6 +89,7 @@ function defineFrameworkConfig( return defineConfig({ entry: ['./src/index.ts'], external: [ + '@angular/core', '@formisch/core', `@formisch/core/${framework}`, '@preact/signals', @@ -113,6 +114,7 @@ function defineFrameworkConfig( } const config: (UserConfig | UserConfigFn)[] = [ + defineFrameworkConfig('angular'), defineFrameworkConfig('preact'), defineFrameworkConfig('qwik'), defineFrameworkConfig('react'), From 699e9764a9966ad7fae166c9a074046e5f096e3c Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 17:53:52 -0400 Subject: [PATCH 03/18] Add @angular/core peer and dev dependency to @formisch/methods --- packages/methods/package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/methods/package.json b/packages/methods/package.json index ec8bd878..6cf16c18 100644 --- a/packages/methods/package.json +++ b/packages/methods/package.json @@ -66,6 +66,7 @@ "build": "tsdown" }, "devDependencies": { + "@angular/core": "^21.0.0", "@formisch/core": "workspace:*", "@formisch/eslint-config": "workspace:*", "@types/node": "24.0.13", @@ -81,11 +82,15 @@ "vitest": "3.2.4" }, "peerDependencies": { + "@angular/core": ">=17.0.0", "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "typescript": ">=5", "valibot": "^1.4.1" }, "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, "react": { "optional": true }, From 02482d10f02364af572919071f0d1ec4421a19df Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 17:59:12 -0400 Subject: [PATCH 04/18] Update lockfile for @angular/core dev dependency --- pnpm-lock.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index accceade..75710d8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -525,6 +525,9 @@ importers: specifier: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 version: 19.2.1 devDependencies: + '@angular/core': + specifier: ^21.0.0 + version: 21.2.13(rxjs@7.8.2)(zone.js@0.15.1) '@formisch/core': specifier: workspace:* version: link:../core From a587099d3bb24cea5368afb082cb5e0ee07adb24 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 17:59:16 -0400 Subject: [PATCH 05/18] Add Angular FormStore, FieldStore, and FieldArrayStore types --- frameworks/angular/src/types/field.ts | 119 ++++++++++++++++++++++++++ frameworks/angular/src/types/form.ts | 40 +++++++++ frameworks/angular/src/types/index.ts | 3 +- 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 frameworks/angular/src/types/field.ts create mode 100644 frameworks/angular/src/types/form.ts diff --git a/frameworks/angular/src/types/field.ts b/frameworks/angular/src/types/field.ts new file mode 100644 index 00000000..7bbe1cef --- /dev/null +++ b/frameworks/angular/src/types/field.ts @@ -0,0 +1,119 @@ +import type { Signal } from '@angular/core'; +import type { + FieldElement, + PartialValues, + PathValue, + RequiredPath, + Schema, + ValidArrayPath, + ValidPath, +} from '@formisch/core/angular'; +import type * as v from 'valibot'; + +/** + * Field element props interface. + */ +export interface FieldElementProps { + /** + * The name attribute of the field element. + */ + readonly name: string; + /** + * Whether to autofocus the field element when there are errors. + */ + readonly autofocus: boolean; + /** + * The ref callback to register the field element. + */ + readonly ref: (element: FieldElement | null) => void; + /** + * The focus event handler of the field element. + */ + readonly onFocus: (event: FocusEvent) => void; + /** + * The change event handler of the field element. + */ + readonly onChange: (event: Event) => void; + /** + * The blur event handler of the field element. + */ + readonly onBlur: (event: FocusEvent) => void; +} + +/** + * Field store interface. + */ +export interface FieldStore< + TSchema extends Schema = Schema, + TFieldPath extends RequiredPath = RequiredPath, +> { + /** + * The path to the field within the form. + */ + readonly path: ValidPath, TFieldPath>; + /** + * The current input value of the field as a reactive signal. + */ + readonly input: Signal< + PartialValues, TFieldPath>> + >; + /** + * The current error messages of the field. + */ + readonly errors: Signal<[string, ...string[]] | null>; + /** + * Whether the field has been touched. + */ + readonly isTouched: Signal; + /** + * Whether the field input differs from its initial value. + */ + readonly isDirty: Signal; + /** + * Whether the field is valid according to the schema. + */ + readonly isValid: Signal; + /** + * Sets the field input value programmatically. + */ + readonly onInput: ( + value: PartialValues, TFieldPath>> + ) => void; + /** + * The props to spread onto the field element for integration. + */ + readonly props: FieldElementProps; +} + +/** + * Field array store interface. + */ +export interface FieldArrayStore< + TSchema extends Schema = Schema, + TFieldArrayPath extends RequiredPath = RequiredPath, +> { + /** + * The path to the array field within the form. + */ + readonly path: ValidArrayPath, TFieldArrayPath>; + /** + * The item IDs of the array field. + */ + readonly items: Signal; + /** + * The current error messages of the field array. + */ + readonly errors: Signal<[string, ...string[]] | null>; + /** + * Whether the field array has been touched. + */ + readonly isTouched: Signal; + /** + * Whether the field array input differs from its initial value. + */ + readonly isDirty: Signal; + /** + * Whether the field array is valid according to the schema. + */ + readonly isValid: Signal; +} diff --git a/frameworks/angular/src/types/form.ts b/frameworks/angular/src/types/form.ts new file mode 100644 index 00000000..bcf6e7c5 --- /dev/null +++ b/frameworks/angular/src/types/form.ts @@ -0,0 +1,40 @@ +import type { Signal } from '@angular/core'; +import type { BaseFormStore, Schema } from '@formisch/core/angular'; + +/** + * Form store interface. + */ +export interface FormStore + extends BaseFormStore { + /** + * Whether the form is currently submitting. + */ + readonly isSubmitting: Signal; + /** + * Whether the form has been submitted. + */ + readonly isSubmitted: Signal; + /** + * Whether the form is currently validating. + */ + readonly isValidating: Signal; + /** + * Whether any field in the form has been touched. + */ + readonly isTouched: Signal; + /** + * Whether any field in the form differs from its initial value. + */ + readonly isDirty: Signal; + /** + * Whether the form is valid according to the schema. + */ + readonly isValid: Signal; + /** + * The current error messages of the form. + * + * Hint: This property only contains validation errors at the root level + * of the form. To get all errors from all fields, use `getAllErrors`. + */ + readonly errors: Signal<[string, ...string[]] | null>; +} diff --git a/frameworks/angular/src/types/index.ts b/frameworks/angular/src/types/index.ts index cb0ff5c3..f0cc7379 100644 --- a/frameworks/angular/src/types/index.ts +++ b/frameworks/angular/src/types/index.ts @@ -1 +1,2 @@ -export {}; +export * from './field.ts'; +export * from './form.ts'; From c7521a949e329030b5cd445e9e2c2f93ad555e16 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 18:03:36 -0400 Subject: [PATCH 06/18] Add injectForm with Signal-based FormStore --- frameworks/angular/src/functions/index.ts | 2 +- .../angular/src/functions/injectForm/index.ts | 1 + .../functions/injectForm/injectForm.test-d.ts | 21 +++++++ .../functions/injectForm/injectForm.test.ts | 58 +++++++++++++++++++ .../src/functions/injectForm/injectForm.ts | 53 +++++++++++++++++ frameworks/angular/src/vitest/setup.ts | 10 +++- 6 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 frameworks/angular/src/functions/injectForm/index.ts create mode 100644 frameworks/angular/src/functions/injectForm/injectForm.test-d.ts create mode 100644 frameworks/angular/src/functions/injectForm/injectForm.test.ts create mode 100644 frameworks/angular/src/functions/injectForm/injectForm.ts diff --git a/frameworks/angular/src/functions/index.ts b/frameworks/angular/src/functions/index.ts index cb0ff5c3..e8ba122f 100644 --- a/frameworks/angular/src/functions/index.ts +++ b/frameworks/angular/src/functions/index.ts @@ -1 +1 @@ -export {}; +export * from './injectForm/index.ts'; diff --git a/frameworks/angular/src/functions/injectForm/index.ts b/frameworks/angular/src/functions/injectForm/index.ts new file mode 100644 index 00000000..7ebb6877 --- /dev/null +++ b/frameworks/angular/src/functions/injectForm/index.ts @@ -0,0 +1 @@ +export * from './injectForm.ts'; diff --git a/frameworks/angular/src/functions/injectForm/injectForm.test-d.ts b/frameworks/angular/src/functions/injectForm/injectForm.test-d.ts new file mode 100644 index 00000000..f34b55e2 --- /dev/null +++ b/frameworks/angular/src/functions/injectForm/injectForm.test-d.ts @@ -0,0 +1,21 @@ +import type { Signal } from '@angular/core'; +import * as v from 'valibot'; +import { expectTypeOf } from 'vitest'; +import type { FormStore } from '../../types/index.ts'; +import { injectForm } from './injectForm.ts'; + +const Schema = v.object({ + email: v.pipe(v.string(), v.email()), + password: v.pipe(v.string(), v.minLength(8)), +}); + +declare const form: ReturnType>; + +expectTypeOf(form).toMatchTypeOf>(); +expectTypeOf(form.isSubmitting).toEqualTypeOf>(); +expectTypeOf(form.isSubmitted).toEqualTypeOf>(); +expectTypeOf(form.isValidating).toEqualTypeOf>(); +expectTypeOf(form.isTouched).toEqualTypeOf>(); +expectTypeOf(form.isDirty).toEqualTypeOf>(); +expectTypeOf(form.isValid).toEqualTypeOf>(); +expectTypeOf(form.errors).toEqualTypeOf>(); diff --git a/frameworks/angular/src/functions/injectForm/injectForm.test.ts b/frameworks/angular/src/functions/injectForm/injectForm.test.ts new file mode 100644 index 00000000..8b777598 --- /dev/null +++ b/frameworks/angular/src/functions/injectForm/injectForm.test.ts @@ -0,0 +1,58 @@ +import { provideExperimentalZonelessChangeDetection } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import * as v from 'valibot'; +import { describe, beforeEach, expect, it } from 'vitest'; +import { injectForm } from './injectForm.ts'; + +const Schema = v.object({ + email: v.pipe(v.string(), v.email()), +}); + +describe('injectForm', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideExperimentalZonelessChangeDetection()], + }); + }); + + function setup() { + return TestBed.runInInjectionContext(() => + injectForm({ schema: Schema }) + ); + } + + it('initializes isSubmitting to false', () => { + const form = setup(); + expect(form.isSubmitting()).toBe(false); + }); + + it('initializes isSubmitted to false', () => { + const form = setup(); + expect(form.isSubmitted()).toBe(false); + }); + + it('initializes isValidating to false', () => { + const form = setup(); + expect(form.isValidating()).toBe(false); + }); + + it('initializes isTouched to false', () => { + const form = setup(); + expect(form.isTouched()).toBe(false); + }); + + it('initializes isDirty to false', () => { + const form = setup(); + expect(form.isDirty()).toBe(false); + }); + + it('initializes isValid to true when no validation errors', () => { + const form = setup(); + expect(form.isValid()).toBe(true); + }); + + it('initializes errors to null', () => { + const form = setup(); + expect(form.errors()).toBeNull(); + }); +}); diff --git a/frameworks/angular/src/functions/injectForm/injectForm.ts b/frameworks/angular/src/functions/injectForm/injectForm.ts new file mode 100644 index 00000000..3fc0da1b --- /dev/null +++ b/frameworks/angular/src/functions/injectForm/injectForm.ts @@ -0,0 +1,53 @@ +import { + afterNextRender, + assertInInjectionContext, + computed, +} from '@angular/core'; +import { + createFormStore, + type FormConfig, + getFieldBool, + INTERNAL, + type Schema, + validateFormInput, +} from '@formisch/core/angular'; +import * as v from 'valibot'; +import type { FormStore } from '../../types/index.ts'; + +/** + * Creates a reactive form store from a form configuration. The form store + * manages form state and provides reactive Signal properties callable in templates. + * + * Must be called in an injection context (component constructor or field initializer). + * + * @param config The form configuration. + * + * @returns The form store with reactive Signal properties. + */ +export function injectForm( + config: FormConfig +): FormStore; + +// @__NO_SIDE_EFFECTS__ +export function injectForm(config: FormConfig): FormStore { + assertInInjectionContext(injectForm); + + const internalFormStore = createFormStore(config, (input: unknown) => + v.safeParseAsync(config.schema, input) + ); + + if (config.validate === 'initial') { + afterNextRender(() => validateFormInput(internalFormStore)); + } + + return { + [INTERNAL]: internalFormStore, + isSubmitting: computed(() => internalFormStore.isSubmitting.value), + isSubmitted: computed(() => internalFormStore.isSubmitted.value), + isValidating: computed(() => internalFormStore.isValidating.value), + isTouched: computed(() => getFieldBool(internalFormStore, 'isTouched')), + isDirty: computed(() => getFieldBool(internalFormStore, 'isDirty')), + isValid: computed(() => !getFieldBool(internalFormStore, 'errors')), + errors: computed(() => internalFormStore.errors.value), + }; +} diff --git a/frameworks/angular/src/vitest/setup.ts b/frameworks/angular/src/vitest/setup.ts index 46b6f57c..4138a686 100644 --- a/frameworks/angular/src/vitest/setup.ts +++ b/frameworks/angular/src/vitest/setup.ts @@ -1,5 +1,13 @@ import { TestBed } from '@angular/core/testing'; -import { afterEach } from 'vitest'; +import { + BrowserTestingModule, + platformBrowserTesting, +} from '@angular/platform-browser/testing'; +import { afterEach, beforeAll } from 'vitest'; + +beforeAll(() => { + TestBed.initTestEnvironment(BrowserTestingModule, platformBrowserTesting()); +}); afterEach(() => { TestBed.resetTestingModule(); From 849d9353af63202dcf582e848c1fd7ee130b8344 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 18:08:53 -0400 Subject: [PATCH 07/18] Add injectField with Signal-based FieldStore --- frameworks/angular/src/functions/index.ts | 1 + .../src/functions/injectField/index.ts | 1 + .../injectField/injectField.test-d.ts | 75 ++++++++++++ .../functions/injectField/injectField.test.ts | 67 ++++++++++ .../src/functions/injectField/injectField.ts | 115 ++++++++++++++++++ 5 files changed, 259 insertions(+) create mode 100644 frameworks/angular/src/functions/injectField/index.ts create mode 100644 frameworks/angular/src/functions/injectField/injectField.test-d.ts create mode 100644 frameworks/angular/src/functions/injectField/injectField.test.ts create mode 100644 frameworks/angular/src/functions/injectField/injectField.ts diff --git a/frameworks/angular/src/functions/index.ts b/frameworks/angular/src/functions/index.ts index e8ba122f..c109cb3a 100644 --- a/frameworks/angular/src/functions/index.ts +++ b/frameworks/angular/src/functions/index.ts @@ -1 +1,2 @@ +export * from './injectField/index.ts'; export * from './injectForm/index.ts'; diff --git a/frameworks/angular/src/functions/injectField/index.ts b/frameworks/angular/src/functions/injectField/index.ts new file mode 100644 index 00000000..84ef33a7 --- /dev/null +++ b/frameworks/angular/src/functions/injectField/index.ts @@ -0,0 +1 @@ +export * from './injectField.ts'; diff --git a/frameworks/angular/src/functions/injectField/injectField.test-d.ts b/frameworks/angular/src/functions/injectField/injectField.test-d.ts new file mode 100644 index 00000000..856212a1 --- /dev/null +++ b/frameworks/angular/src/functions/injectField/injectField.test-d.ts @@ -0,0 +1,75 @@ +import type { Signal } from '@angular/core'; +import * as v from 'valibot'; +import { describe, expectTypeOf, test } from 'vitest'; +import type { FieldStore } from '../../types/index.ts'; +import { injectForm } from '../injectForm/index.ts'; +import { injectField } from './injectField.ts'; + +const schemaSimple = v.object({ name: v.string() }); +const schemaMixed = v.object({ name: v.string(), age: v.number() }); +const schemaEmail = v.object({ email: v.pipe(v.string(), v.email()) }); +const schemaNested = v.object({ + user: v.object({ email: v.string() }), + tags: v.array(v.string()), +}); + +declare const formSimple: ReturnType>; +declare const formMixed: ReturnType>; +declare const formEmail: ReturnType>; +declare const formNested: ReturnType>; + +describe('injectField', () => { + test('should return a FieldStore typed against the form schema and path', () => { + const field = injectField(formSimple, { path: ['name'] }); + + expectTypeOf(field).toEqualTypeOf>(); + }); + + test('should narrow input type for primitive leaves', () => { + expectTypeOf(injectField(formMixed, { path: ['name'] }).input).toEqualTypeOf< + Signal + >(); + expectTypeOf(injectField(formMixed, { path: ['age'] }).input).toEqualTypeOf< + Signal + >(); + }); + + test('should narrow onInput value type for primitive leaves', () => { + expectTypeOf( + injectField(formSimple, { path: ['name'] }).onInput + ).toEqualTypeOf<(value: string | undefined) => void>(); + }); + + test('should narrow input type through nested object and array index paths', () => { + expectTypeOf( + injectField(formNested, { path: ['user', 'email'] }).input + ).toEqualTypeOf>(); + expectTypeOf( + injectField(formNested, { path: ['tags', 0] }).input + ).toEqualTypeOf>(); + }); + + test('should expose Signal boolean state properties', () => { + const field = injectField(formEmail, { path: ['email'] }); + + expectTypeOf(field.errors).toEqualTypeOf>(); + expectTypeOf(field.isTouched).toEqualTypeOf>(); + expectTypeOf(field.isDirty).toEqualTypeOf>(); + expectTypeOf(field.isValid).toEqualTypeOf>(); + }); + + test('should expose props with name and autofocus', () => { + const field = injectField(formEmail, { path: ['email'] }); + + expectTypeOf(field.props.name).toEqualTypeOf(); + expectTypeOf(field.props.autofocus).toEqualTypeOf(); + }); + + test('should reject invalid paths', () => { + // @ts-expect-error nonexistent field + injectField(formSimple, { path: ['nonexistent'] }); + + // @ts-expect-error path through a string leaf + injectField(formSimple, { path: ['name', 'nested'] }); + }); +}); diff --git a/frameworks/angular/src/functions/injectField/injectField.test.ts b/frameworks/angular/src/functions/injectField/injectField.test.ts new file mode 100644 index 00000000..8a343bb7 --- /dev/null +++ b/frameworks/angular/src/functions/injectField/injectField.test.ts @@ -0,0 +1,67 @@ +import { provideExperimentalZonelessChangeDetection } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import * as v from 'valibot'; +import { describe, beforeEach, expect, it } from 'vitest'; +import { injectForm } from '../injectForm/index.ts'; +import { injectField } from './injectField.ts'; + +const Schema = v.object({ + email: v.pipe(v.string(), v.email()), +}); + +describe('injectField', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideExperimentalZonelessChangeDetection()], + }); + }); + + function setup() { + return TestBed.runInInjectionContext(() => { + const form = injectForm({ schema: Schema }); + const field = injectField(form, { path: ['email'] }); + return { form, field }; + }); + } + + it('returns the correct path', () => { + const { field } = setup(); + expect(field.path).toEqual(['email']); + }); + + it('initializes input as a signal with undefined', () => { + const { field } = setup(); + expect(field.input()).toBeUndefined(); + }); + + it('initializes errors to null', () => { + const { field } = setup(); + expect(field.errors()).toBeNull(); + }); + + it('initializes isTouched to false', () => { + const { field } = setup(); + expect(field.isTouched()).toBe(false); + }); + + it('initializes isDirty to false', () => { + const { field } = setup(); + expect(field.isDirty()).toBe(false); + }); + + it('initializes isValid to true', () => { + const { field } = setup(); + expect(field.isValid()).toBe(true); + }); + + it('updates input signal when onInput is called', () => { + const { field } = setup(); + field.onInput('test@example.com'); + expect(field.input()).toBe('test@example.com'); + }); + + it('exposes a name prop as JSON-stringified path', () => { + const { field } = setup(); + expect(field.props.name).toBe('["email"]'); + }); +}); diff --git a/frameworks/angular/src/functions/injectField/injectField.ts b/frameworks/angular/src/functions/injectField/injectField.ts new file mode 100644 index 00000000..ea5bcc34 --- /dev/null +++ b/frameworks/angular/src/functions/injectField/injectField.ts @@ -0,0 +1,115 @@ +import { + assertInInjectionContext, + computed, + DestroyRef, + inject, +} from '@angular/core'; +import { + type FieldElement, + getElementInput, + getFieldBool, + getFieldInput, + getFieldStore, + INTERNAL, + type RequiredPath, + type Schema, + setFieldBool, + setFieldInput, + validateIfRequired, + type ValidPath, +} from '@formisch/core/angular'; +import type * as v from 'valibot'; +import type { FieldStore, FormStore } from '../../types/index.ts'; + +/** + * Inject field config interface. + */ +export interface InjectFieldConfig< + TSchema extends Schema = Schema, + TFieldPath extends RequiredPath = RequiredPath, +> { + /** + * The path to the field within the form schema. + */ + readonly path: ValidPath, TFieldPath>; +} + +/** + * Creates a reactive field store for a specific field within a form store. + * Exposes all reactive state as Angular Signals callable in templates. + * + * Must be called in an injection context (component constructor or field initializer). + * + * @param form The form store instance. + * @param config The field configuration. + * + * @returns The field store with reactive Signal properties and element props. + */ +export function injectField< + TSchema extends Schema, + TFieldPath extends RequiredPath, +>( + form: FormStore, + config: InjectFieldConfig +): FieldStore; + +// @__NO_SIDE_EFFECTS__ +export function injectField( + form: FormStore, + config: InjectFieldConfig +): FieldStore { + assertInInjectionContext(injectField); + + const destroyRef = inject(DestroyRef); + const internalFormStore = form[INTERNAL]; + const internalFieldStore = getFieldStore(internalFormStore, config.path); + + destroyRef.onDestroy(() => { + internalFieldStore.elements = internalFieldStore.elements.filter( + (element) => element.isConnected + ); + }); + + return { + path: config.path, + input: computed(() => getFieldInput(internalFieldStore)), + errors: computed(() => internalFieldStore.errors.value), + isTouched: computed(() => getFieldBool(internalFieldStore, 'isTouched')), + isDirty: computed(() => getFieldBool(internalFieldStore, 'isDirty')), + isValid: computed(() => !getFieldBool(internalFieldStore, 'errors')), + onInput(value) { + setFieldInput(internalFormStore, config.path, value); + validateIfRequired(internalFormStore, internalFieldStore, 'input'); + }, + props: { + get name() { + return internalFieldStore.name; + }, + autofocus: !!internalFieldStore.errors.value, + ref(element) { + if (element) { + internalFieldStore.elements.push(element as FieldElement); + } + }, + onFocus() { + setFieldBool(internalFieldStore, 'isTouched', true); + validateIfRequired(internalFormStore, internalFieldStore, 'touch'); + }, + onChange(event) { + setFieldInput( + internalFormStore, + config.path, + getElementInput( + event.currentTarget as FieldElement, + internalFieldStore + ) + ); + validateIfRequired(internalFormStore, internalFieldStore, 'input'); + validateIfRequired(internalFormStore, internalFieldStore, 'change'); + }, + onBlur() { + validateIfRequired(internalFormStore, internalFieldStore, 'blur'); + }, + }, + }; +} From 8db026758e3cf9522d18f4820bf67d60348a74b1 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 18:10:53 -0400 Subject: [PATCH 08/18] Add injectFieldArray with Signal-based FieldArrayStore --- frameworks/angular/src/functions/index.ts | 1 + .../src/functions/injectFieldArray/index.ts | 1 + .../injectFieldArray.test-d.ts | 25 +++++++ .../injectFieldArray/injectFieldArray.test.ts | 58 +++++++++++++++ .../injectFieldArray/injectFieldArray.ts | 70 +++++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 frameworks/angular/src/functions/injectFieldArray/index.ts create mode 100644 frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts create mode 100644 frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test.ts create mode 100644 frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts diff --git a/frameworks/angular/src/functions/index.ts b/frameworks/angular/src/functions/index.ts index c109cb3a..7d1e0f9d 100644 --- a/frameworks/angular/src/functions/index.ts +++ b/frameworks/angular/src/functions/index.ts @@ -1,2 +1,3 @@ export * from './injectField/index.ts'; +export * from './injectFieldArray/index.ts'; export * from './injectForm/index.ts'; diff --git a/frameworks/angular/src/functions/injectFieldArray/index.ts b/frameworks/angular/src/functions/injectFieldArray/index.ts new file mode 100644 index 00000000..be7a9c75 --- /dev/null +++ b/frameworks/angular/src/functions/injectFieldArray/index.ts @@ -0,0 +1 @@ +export * from './injectFieldArray.ts'; diff --git a/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts new file mode 100644 index 00000000..bfc2857c --- /dev/null +++ b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts @@ -0,0 +1,25 @@ +import type { Signal } from '@angular/core'; +import * as v from 'valibot'; +import { expectTypeOf } from 'vitest'; +import type { FieldArrayStore } from '../../types/index.ts'; +import { injectFieldArray } from './injectFieldArray.ts'; + +const Schema = v.object({ + todos: v.array(v.object({ title: v.string() })), +}); + +declare const fieldArray: ReturnType< + typeof injectFieldArray +>; + +expectTypeOf(fieldArray).toMatchTypeOf< + FieldArrayStore +>(); +expectTypeOf(fieldArray.path).toEqualTypeOf<['todos']>(); +expectTypeOf(fieldArray.items).toEqualTypeOf>(); +expectTypeOf(fieldArray.errors).toEqualTypeOf< + Signal<[string, ...string[]] | null> +>(); +expectTypeOf(fieldArray.isTouched).toEqualTypeOf>(); +expectTypeOf(fieldArray.isDirty).toEqualTypeOf>(); +expectTypeOf(fieldArray.isValid).toEqualTypeOf>(); diff --git a/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test.ts b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test.ts new file mode 100644 index 00000000..908ef57c --- /dev/null +++ b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test.ts @@ -0,0 +1,58 @@ +import { + provideExperimentalZonelessChangeDetection, +} from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import * as v from 'valibot'; +import { describe, beforeEach, expect, it } from 'vitest'; +import { injectForm } from '../injectForm/index.ts'; +import { injectFieldArray } from './injectFieldArray.ts'; + +const Schema = v.object({ + todos: v.array(v.object({ title: v.string() })), +}); + +describe('injectFieldArray', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideExperimentalZonelessChangeDetection()], + }); + }); + + function setup() { + return TestBed.runInInjectionContext(() => { + const form = injectForm({ schema: Schema }); + const fieldArray = injectFieldArray(form, { path: ['todos'] }); + return { form, fieldArray }; + }); + } + + it('returns the correct path', () => { + const { fieldArray } = setup(); + expect(fieldArray.path).toEqual(['todos']); + }); + + it('initializes items as an empty signal array', () => { + const { fieldArray } = setup(); + expect(fieldArray.items()).toEqual([]); + }); + + it('initializes errors to null', () => { + const { fieldArray } = setup(); + expect(fieldArray.errors()).toBeNull(); + }); + + it('initializes isTouched to false', () => { + const { fieldArray } = setup(); + expect(fieldArray.isTouched()).toBe(false); + }); + + it('initializes isDirty to false', () => { + const { fieldArray } = setup(); + expect(fieldArray.isDirty()).toBe(false); + }); + + it('initializes isValid to true', () => { + const { fieldArray } = setup(); + expect(fieldArray.isValid()).toBe(true); + }); +}); diff --git a/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts new file mode 100644 index 00000000..44d517ae --- /dev/null +++ b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts @@ -0,0 +1,70 @@ +import { + assertInInjectionContext, + computed, +} from '@angular/core'; +import { + getFieldBool, + getFieldStore, + INTERNAL, + type InternalArrayStore, + type RequiredPath, + type Schema, + type ValidArrayPath, +} from '@formisch/core/angular'; +import type * as v from 'valibot'; +import type { FieldArrayStore, FormStore } from '../../types/index.ts'; + +/** + * Inject field array config interface. + */ +export interface InjectFieldArrayConfig< + TSchema extends Schema = Schema, + TFieldArrayPath extends RequiredPath = RequiredPath, +> { + /** + * The path to the field array within the form schema. + */ + readonly path: ValidArrayPath, TFieldArrayPath>; +} + +/** + * Creates a reactive field array store for a specific array field within a form store. + * Exposes all reactive state as Angular Signals callable in templates. + * + * Must be called in an injection context (component constructor or field initializer). + * + * @param form The form store instance. + * @param config The field array configuration. + * + * @returns The field array store with reactive Signal properties. + */ +// @ts-expect-error +export function injectFieldArray< + TSchema extends Schema, + TFieldArrayPath extends RequiredPath, +>( + form: FormStore, + config: InjectFieldArrayConfig +): FieldArrayStore; + +// @__NO_SIDE_EFFECTS__ +export function injectFieldArray( + form: FormStore, + config: InjectFieldArrayConfig +): FieldArrayStore { + assertInInjectionContext(injectFieldArray); + + const internalFieldStore = getFieldStore( + form[INTERNAL], + config.path + ) as InternalArrayStore; + + return { + path: config.path, + items: computed(() => internalFieldStore.items.value), + errors: computed(() => internalFieldStore.errors.value), + isTouched: computed(() => getFieldBool(internalFieldStore, 'isTouched')), + isDirty: computed(() => getFieldBool(internalFieldStore, 'isDirty')), + isValid: computed(() => !getFieldBool(internalFieldStore, 'errors')), + }; +} From 81062c3c7f8bb41cf7e3d570ba13aab21c17b5c6 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 18:22:58 -0400 Subject: [PATCH 09/18] Add FormischForm standalone component --- .../FormischForm/FormischForm.test.ts | 53 ++++++++++++++++++ .../components/FormischForm/FormischForm.ts | 55 +++++++++++++++++++ .../src/components/FormischForm/index.ts | 1 + frameworks/angular/src/components/index.ts | 2 +- 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 frameworks/angular/src/components/FormischForm/FormischForm.test.ts create mode 100644 frameworks/angular/src/components/FormischForm/FormischForm.ts create mode 100644 frameworks/angular/src/components/FormischForm/index.ts diff --git a/frameworks/angular/src/components/FormischForm/FormischForm.test.ts b/frameworks/angular/src/components/FormischForm/FormischForm.test.ts new file mode 100644 index 00000000..d61d300c --- /dev/null +++ b/frameworks/angular/src/components/FormischForm/FormischForm.test.ts @@ -0,0 +1,53 @@ +import { Component, provideExperimentalZonelessChangeDetection } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import * as v from 'valibot'; +import { describe, beforeEach, expect, it, vi } from 'vitest'; +import { injectForm } from '../../functions/index.ts'; +import { FormischForm } from './FormischForm.ts'; + +const Schema = v.object({ email: v.pipe(v.string(), v.email()) }); + +@Component({ + standalone: true, + imports: [FormischForm], + template: ` + + + + `, +}) +class TestHost { + form = injectForm({ schema: Schema }); + handleSubmit = vi.fn(); +} + +describe('FormischForm', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [TestHost], + providers: [provideExperimentalZonelessChangeDetection()], + }); + }); + + it('renders a native form element', async () => { + const fixture = TestBed.createComponent(TestHost); + await fixture.whenStable(); + const form = fixture.nativeElement.querySelector('form'); + expect(form).not.toBeNull(); + }); + + it('sets novalidate on the form element', async () => { + const fixture = TestBed.createComponent(TestHost); + await fixture.whenStable(); + const form = fixture.nativeElement.querySelector('form'); + expect(form.hasAttribute('novalidate')).toBe(true); + }); + + it('registers the form element on the internal store', async () => { + const fixture = TestBed.createComponent(TestHost); + await fixture.whenStable(); + const { INTERNAL } = await import('@formisch/core/angular'); + const internalStore = fixture.componentInstance.form[INTERNAL]; + expect(internalStore.element).toBeInstanceOf(HTMLFormElement); + }); +}); diff --git a/frameworks/angular/src/components/FormischForm/FormischForm.ts b/frameworks/angular/src/components/FormischForm/FormischForm.ts new file mode 100644 index 00000000..0f5b242c --- /dev/null +++ b/frameworks/angular/src/components/FormischForm/FormischForm.ts @@ -0,0 +1,55 @@ +import { + afterNextRender, + Component, + ElementRef, + inject, + Input, + input, + type InputSignal, +} from '@angular/core'; +import { + INTERNAL, + type Schema, + type SubmitEventHandler, +} from '@formisch/core/angular'; +import { handleSubmit } from '@formisch/methods/angular'; +import type { FormStore } from '../../types/index.ts'; + +/** + * Form component that manages form submission and registers the form element. + * Wraps the native form element and delegates submission to handleSubmit. + * + * @example + * ```html + * + * ... + * + * ``` + */ +@Component({ + selector: 'formisch-form', + standalone: true, + template: `
`, +}) +export class FormischForm { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input({ isSignal: true } as any) of: InputSignal> = input.required>(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input({ isSignal: true } as any) submitFn: InputSignal> = input.required>(); + + private readonly hostEl: ElementRef = inject(ElementRef); + + constructor() { + afterNextRender(() => { + const formElement = + this.hostEl.nativeElement.querySelector('form'); + if (formElement) { + this.of()[INTERNAL].element = formElement; + } + }); + } + + protected handleFormSubmit(event: SubmitEvent): void { + handleSubmit(this.of(), this.submitFn())(event); + } +} diff --git a/frameworks/angular/src/components/FormischForm/index.ts b/frameworks/angular/src/components/FormischForm/index.ts new file mode 100644 index 00000000..758a4e87 --- /dev/null +++ b/frameworks/angular/src/components/FormischForm/index.ts @@ -0,0 +1 @@ +export * from './FormischForm.ts'; diff --git a/frameworks/angular/src/components/index.ts b/frameworks/angular/src/components/index.ts index cb0ff5c3..5f1957a5 100644 --- a/frameworks/angular/src/components/index.ts +++ b/frameworks/angular/src/components/index.ts @@ -1 +1 @@ -export {}; +export * from './FormischForm/index.ts'; From dcfe62bec34e16bc455eaa6c3d5d9ae4ef193dc8 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 18:27:49 -0400 Subject: [PATCH 10/18] Add FormischField standalone component with TemplateRef content projection --- .../FormischField/FormischField.test.ts | 47 ++++++ .../components/FormischField/FormischField.ts | 158 ++++++++++++++++++ .../src/components/FormischField/index.ts | 1 + 3 files changed, 206 insertions(+) create mode 100644 frameworks/angular/src/components/FormischField/FormischField.test.ts create mode 100644 frameworks/angular/src/components/FormischField/FormischField.ts create mode 100644 frameworks/angular/src/components/FormischField/index.ts diff --git a/frameworks/angular/src/components/FormischField/FormischField.test.ts b/frameworks/angular/src/components/FormischField/FormischField.test.ts new file mode 100644 index 00000000..80fa18c7 --- /dev/null +++ b/frameworks/angular/src/components/FormischField/FormischField.test.ts @@ -0,0 +1,47 @@ +import { Component, provideExperimentalZonelessChangeDetection } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import * as v from 'valibot'; +import { describe, beforeEach, expect, it } from 'vitest'; +import { injectForm } from '../../functions/index.ts'; +import { FormischField } from './FormischField.ts'; + +const Schema = v.object({ email: v.pipe(v.string(), v.email()) }); + +@Component({ + standalone: true, + imports: [FormischField], + template: ` + + + + {{ field.errors() }} + + + `, +}) +class TestHost { + form = injectForm({ schema: Schema }); +} + +describe('FormischField', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [TestHost], + providers: [provideExperimentalZonelessChangeDetection()], + }); + }); + + it('renders the template with the field store', async () => { + const fixture = TestBed.createComponent(TestHost); + await fixture.whenStable(); + const input = fixture.nativeElement.querySelector('[data-testid="input"]'); + expect(input).not.toBeNull(); + }); + + it('passes the field name prop to the template context', async () => { + const fixture = TestBed.createComponent(TestHost); + await fixture.whenStable(); + const input = fixture.nativeElement.querySelector('[data-testid="input"]'); + expect(input.getAttribute('name')).not.toBeNull(); + }); +}); diff --git a/frameworks/angular/src/components/FormischField/FormischField.ts b/frameworks/angular/src/components/FormischField/FormischField.ts new file mode 100644 index 00000000..a6267b47 --- /dev/null +++ b/frameworks/angular/src/components/FormischField/FormischField.ts @@ -0,0 +1,158 @@ +import { + Component, + computed, + ContentChild, + DestroyRef, + inject, + Input, + input, + type InputSignal, + TemplateRef, +} from '@angular/core'; +import { NgIf, NgTemplateOutlet } from '@angular/common'; +import { + type FieldElement, + getElementInput, + getFieldBool, + getFieldInput, + getFieldStore, + INTERNAL, + type RequiredPath, + type Schema, + setFieldBool, + setFieldInput, + validateIfRequired, + type ValidPath, +} from '@formisch/core/angular'; +import type * as v from 'valibot'; +import type { FieldStore, FormStore } from '../../types/index.ts'; + +/** + * Headless field component that provides reactive field state via an Angular template. + * Uses ContentChild TemplateRef pattern to pass the FieldStore as $implicit context. + * + * @example + * ```html + * + * + * + * + * + * ``` + */ +@Component({ + selector: 'formisch-field', + standalone: true, + imports: [NgIf, NgTemplateOutlet], + template: ` + + `, +}) +export class FormischField< + TSchema extends Schema = Schema, + TFieldPath extends RequiredPath = RequiredPath, +> { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input({ isSignal: true } as any) of: InputSignal> = input.required>(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input({ isSignal: true } as any) path: InputSignal, TFieldPath>> = input.required, TFieldPath>>(); + + @ContentChild(TemplateRef) protected template: TemplateRef<{ $implicit: FieldStore }> | undefined; + + private readonly internalFormStore = computed(() => this.of()[INTERNAL]); + private readonly internalFieldStore = computed(() => + getFieldStore(this.internalFormStore(), this.path()) + ); + private readonly destroyRef = inject(DestroyRef); + + protected readonly field: FieldStore = (() => { + const internalFormStore = this.internalFormStore; + const internalFieldStore = this.internalFieldStore; + const pathSignal = this.path; + const destroyRef = this.destroyRef; + + destroyRef.onDestroy(() => { + internalFieldStore().elements = internalFieldStore().elements.filter( + (element) => element.isConnected + ); + }); + + return { + get path() { + return pathSignal(); + }, + input: computed(() => getFieldInput(internalFieldStore())), + errors: computed(() => internalFieldStore().errors.value), + isTouched: computed(() => + getFieldBool(internalFieldStore(), 'isTouched') + ), + isDirty: computed(() => getFieldBool(internalFieldStore(), 'isDirty')), + isValid: computed( + () => !getFieldBool(internalFieldStore(), 'errors') + ), + onInput(value) { + setFieldInput(internalFormStore(), pathSignal(), value); + validateIfRequired( + internalFormStore(), + internalFieldStore(), + 'input' + ); + }, + props: { + get name() { + return internalFieldStore().name; + }, + get autofocus() { + return !!internalFieldStore().errors.value; + }, + ref(element) { + if (element) { + internalFieldStore().elements.push(element as FieldElement); + } + }, + onFocus() { + setFieldBool(internalFieldStore(), 'isTouched', true); + validateIfRequired( + internalFormStore(), + internalFieldStore(), + 'touch' + ); + }, + onChange(event) { + setFieldInput( + internalFormStore(), + pathSignal(), + getElementInput( + event.currentTarget as FieldElement, + internalFieldStore() + ) + ); + validateIfRequired( + internalFormStore(), + internalFieldStore(), + 'input' + ); + validateIfRequired( + internalFormStore(), + internalFieldStore(), + 'change' + ); + }, + onBlur() { + validateIfRequired( + internalFormStore(), + internalFieldStore(), + 'blur' + ); + }, + }, + }; + })() as FieldStore; +} diff --git a/frameworks/angular/src/components/FormischField/index.ts b/frameworks/angular/src/components/FormischField/index.ts new file mode 100644 index 00000000..ab831c49 --- /dev/null +++ b/frameworks/angular/src/components/FormischField/index.ts @@ -0,0 +1 @@ +export * from './FormischField.ts'; From ec6bb0883cfa102139a34e5938e88f1c9a872fc5 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 23:04:15 -0400 Subject: [PATCH 11/18] Add FormischFieldArray standalone component --- .../FormischFieldArray.test.ts | 48 ++++++++++ .../FormischFieldArray/FormischFieldArray.ts | 88 +++++++++++++++++++ .../components/FormischFieldArray/index.ts | 1 + 3 files changed, 137 insertions(+) create mode 100644 frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.test.ts create mode 100644 frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts create mode 100644 frameworks/angular/src/components/FormischFieldArray/index.ts diff --git a/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.test.ts b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.test.ts new file mode 100644 index 00000000..28f88f3b --- /dev/null +++ b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.test.ts @@ -0,0 +1,48 @@ +import { Component, provideExperimentalZonelessChangeDetection } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import * as v from 'valibot'; +import { describe, beforeEach, expect, it } from 'vitest'; +import { injectForm } from '../../functions/index.ts'; +import { FormischFieldArray } from './FormischFieldArray.ts'; + +const Schema = v.object({ + todos: v.array(v.object({ title: v.string() })), +}); + +@Component({ + standalone: true, + imports: [FormischFieldArray], + template: ` + + + {{ fieldArray.items().length }} + + + `, +}) +class TestHost { + form = injectForm({ schema: Schema }); +} + +describe('FormischFieldArray', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [TestHost], + providers: [provideExperimentalZonelessChangeDetection()], + }); + }); + + it('renders the template with the field array store', async () => { + const fixture = TestBed.createComponent(TestHost); + await fixture.whenStable(); + const span = fixture.nativeElement.querySelector('[data-testid="count"]'); + expect(span).not.toBeNull(); + }); + + it('passes items signal with initial empty array', async () => { + const fixture = TestBed.createComponent(TestHost); + await fixture.whenStable(); + const span = fixture.nativeElement.querySelector('[data-testid="count"]'); + expect(span.textContent.trim()).toBe('0'); + }); +}); diff --git a/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts new file mode 100644 index 00000000..b44dfdf0 --- /dev/null +++ b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts @@ -0,0 +1,88 @@ +import { + Component, + computed, + ContentChild, + Input, + input, + type InputSignal, + TemplateRef, +} from '@angular/core'; +import { NgIf, NgTemplateOutlet } from '@angular/common'; +import { + getFieldBool, + getFieldStore, + INTERNAL, + type InternalArrayStore, + type RequiredPath, + type Schema, + type ValidArrayPath, +} from '@formisch/core/angular'; +import type * as v from 'valibot'; +import type { FieldArrayStore, FormStore } from '../../types/index.ts'; + +/** + * Headless field array component that provides reactive field array state via an Angular template. + * Uses ContentChild TemplateRef pattern to pass the FieldArrayStore as $implicit context. + * + * @example + * ```html + * + * + * @for (item of fieldArray.items(); track item) { + * ... + * } + * + * + * ``` + */ +@Component({ + selector: 'formisch-field-array', + standalone: true, + imports: [NgIf, NgTemplateOutlet], + template: ` + + `, +}) +export class FormischFieldArray< + TSchema extends Schema = Schema, + TFieldArrayPath extends RequiredPath = RequiredPath, +> { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input({ isSignal: true } as any) of: InputSignal> = input.required>(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input({ isSignal: true } as any) path: InputSignal, TFieldArrayPath>> = input.required, TFieldArrayPath>>(); + + @ContentChild(TemplateRef) protected template: TemplateRef<{ $implicit: FieldArrayStore }> | undefined; + + private readonly internalFieldStore = computed( + () => + getFieldStore(this.of()[INTERNAL], this.path()) as InternalArrayStore + ); + + protected readonly fieldArray: FieldArrayStore = + (() => { + const internalFieldStore = this.internalFieldStore; + const pathSignal = this.path; + + return { + get path() { + return pathSignal(); + }, + items: computed(() => internalFieldStore().items.value), + errors: computed(() => internalFieldStore().errors.value), + isTouched: computed(() => + getFieldBool(internalFieldStore(), 'isTouched') + ), + isDirty: computed(() => + getFieldBool(internalFieldStore(), 'isDirty') + ), + isValid: computed( + () => !getFieldBool(internalFieldStore(), 'errors') + ), + }; + })() as FieldArrayStore; +} diff --git a/frameworks/angular/src/components/FormischFieldArray/index.ts b/frameworks/angular/src/components/FormischFieldArray/index.ts new file mode 100644 index 00000000..b08eb34b --- /dev/null +++ b/frameworks/angular/src/components/FormischFieldArray/index.ts @@ -0,0 +1 @@ +export * from './FormischFieldArray.ts'; From 6038a8371748b8767ae333c620899fa69d06cdb6 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 23:04:20 -0400 Subject: [PATCH 12/18] Convert components to signal inputs and contentChild() query --- .../components/FormischField/FormischField.ts | 33 +++++++++---------- .../components/FormischForm/FormischForm.ts | 10 +++--- .../src/functions/injectField/injectField.ts | 2 +- .../src/functions/injectForm/injectForm.ts | 4 ++- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/frameworks/angular/src/components/FormischField/FormischField.ts b/frameworks/angular/src/components/FormischField/FormischField.ts index a6267b47..16f75e79 100644 --- a/frameworks/angular/src/components/FormischField/FormischField.ts +++ b/frameworks/angular/src/components/FormischField/FormischField.ts @@ -1,15 +1,15 @@ import { Component, computed, - ContentChild, + contentChild, DestroyRef, inject, - Input, input, type InputSignal, - TemplateRef, + type Signal, + TemplateRef } from '@angular/core'; -import { NgIf, NgTemplateOutlet } from '@angular/common'; +import { NgTemplateOutlet } from '@angular/common'; import { type FieldElement, getElementInput, @@ -46,25 +46,24 @@ import type { FieldStore, FormStore } from '../../types/index.ts'; @Component({ selector: 'formisch-field', standalone: true, - imports: [NgIf, NgTemplateOutlet], + imports: [NgTemplateOutlet], template: ` - - `, + @if (template()) { + + } +`, }) export class FormischField< TSchema extends Schema = Schema, TFieldPath extends RequiredPath = RequiredPath, > { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input({ isSignal: true } as any) of: InputSignal> = input.required>(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input({ isSignal: true } as any) path: InputSignal, TFieldPath>> = input.required, TFieldPath>>(); + readonly of: InputSignal> = input.required>(); + readonly path: InputSignal, TFieldPath>> = input.required, TFieldPath>>(); - @ContentChild(TemplateRef) protected template: TemplateRef<{ $implicit: FieldStore }> | undefined; + protected readonly template: Signal | undefined> = contentChild(TemplateRef); private readonly internalFormStore = computed(() => this.of()[INTERNAL]); private readonly internalFieldStore = computed(() => @@ -114,7 +113,7 @@ export class FormischField< }, ref(element) { if (element) { - internalFieldStore().elements.push(element as FieldElement); + internalFieldStore().elements.push(element); } }, onFocus() { diff --git a/frameworks/angular/src/components/FormischForm/FormischForm.ts b/frameworks/angular/src/components/FormischForm/FormischForm.ts index 0f5b242c..7b617383 100644 --- a/frameworks/angular/src/components/FormischForm/FormischForm.ts +++ b/frameworks/angular/src/components/FormischForm/FormischForm.ts @@ -3,7 +3,6 @@ import { Component, ElementRef, inject, - Input, input, type InputSignal, } from '@angular/core'; @@ -32,11 +31,10 @@ import type { FormStore } from '../../types/index.ts'; template: `
`, }) export class FormischForm { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input({ isSignal: true } as any) of: InputSignal> = input.required>(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input({ isSignal: true } as any) submitFn: InputSignal> = input.required>(); + readonly of: InputSignal> = input.required>(); + readonly submitFn: InputSignal> = input.required>(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment private readonly hostEl: ElementRef = inject(ElementRef); constructor() { @@ -50,6 +48,6 @@ export class FormischForm { } protected handleFormSubmit(event: SubmitEvent): void { - handleSubmit(this.of(), this.submitFn())(event); + void handleSubmit(this.of(), this.submitFn())(event); } } diff --git a/frameworks/angular/src/functions/injectField/injectField.ts b/frameworks/angular/src/functions/injectField/injectField.ts index ea5bcc34..a7ba9bed 100644 --- a/frameworks/angular/src/functions/injectField/injectField.ts +++ b/frameworks/angular/src/functions/injectField/injectField.ts @@ -88,7 +88,7 @@ export function injectField( autofocus: !!internalFieldStore.errors.value, ref(element) { if (element) { - internalFieldStore.elements.push(element as FieldElement); + internalFieldStore.elements.push(element); } }, onFocus() { diff --git a/frameworks/angular/src/functions/injectForm/injectForm.ts b/frameworks/angular/src/functions/injectForm/injectForm.ts index 3fc0da1b..5801b6d3 100644 --- a/frameworks/angular/src/functions/injectForm/injectForm.ts +++ b/frameworks/angular/src/functions/injectForm/injectForm.ts @@ -37,7 +37,9 @@ export function injectForm(config: FormConfig): FormStore { ); if (config.validate === 'initial') { - afterNextRender(() => validateFormInput(internalFormStore)); + afterNextRender(() => { + void validateFormInput(internalFormStore); + }); } return { From 7b3841afc5be0d7ad38e0a9941e00be01b639957 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 23:04:52 -0400 Subject: [PATCH 13/18] Wire up src/index.ts with all public exports --- frameworks/angular/src/components/index.ts | 2 ++ frameworks/angular/src/index.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/frameworks/angular/src/components/index.ts b/frameworks/angular/src/components/index.ts index 5f1957a5..f72ea479 100644 --- a/frameworks/angular/src/components/index.ts +++ b/frameworks/angular/src/components/index.ts @@ -1 +1,3 @@ +export * from './FormischField/index.ts'; +export * from './FormischFieldArray/index.ts'; export * from './FormischForm/index.ts'; diff --git a/frameworks/angular/src/index.ts b/frameworks/angular/src/index.ts index bf9d0a99..d67a363c 100644 --- a/frameworks/angular/src/index.ts +++ b/frameworks/angular/src/index.ts @@ -1,3 +1,18 @@ +export type { + DeepPartial, + FieldElement, + FormConfig, + PartialValues, + PathValue, + RequiredPath, + Schema, + SubmitEventHandler, + SubmitHandler, + ValidArrayPath, + ValidationMode, + ValidPath, +} from '@formisch/core/angular'; +export * from '@formisch/methods/angular'; export * from './components/index.ts'; export * from './functions/index.ts'; export * from './types/index.ts'; From 15841fe1ae4fc22afffc222ea46b91965efb14bd Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 23:05:20 -0400 Subject: [PATCH 14/18] Add Angular playground with login, payment, and todos routes --- playgrounds/angular/index.html | 17 + playgrounds/angular/package.json | 31 + playgrounds/angular/src/app.component.ts | 33 + playgrounds/angular/src/app.config.ts | 10 + playgrounds/angular/src/app.routes.ts | 26 + .../src/components/form-footer.component.ts | 31 + .../src/components/form-header.component.ts | 37 + .../src/components/text-input.component.ts | 65 + playgrounds/angular/src/global.css | 26 + playgrounds/angular/src/main.ts | 6 + .../src/routes/login/login.component.ts | 79 + .../src/routes/payment/payment.component.ts | 181 ++ .../src/routes/todos/todos.component.ts | 172 ++ playgrounds/angular/tsconfig.app.json | 32 + playgrounds/angular/tsconfig.json | 6 + playgrounds/angular/vite.config.ts | 15 + pnpm-lock.yaml | 1843 ++++++++++++++++- 17 files changed, 2547 insertions(+), 63 deletions(-) create mode 100644 playgrounds/angular/index.html create mode 100644 playgrounds/angular/package.json create mode 100644 playgrounds/angular/src/app.component.ts create mode 100644 playgrounds/angular/src/app.config.ts create mode 100644 playgrounds/angular/src/app.routes.ts create mode 100644 playgrounds/angular/src/components/form-footer.component.ts create mode 100644 playgrounds/angular/src/components/form-header.component.ts create mode 100644 playgrounds/angular/src/components/text-input.component.ts create mode 100644 playgrounds/angular/src/global.css create mode 100644 playgrounds/angular/src/main.ts create mode 100644 playgrounds/angular/src/routes/login/login.component.ts create mode 100644 playgrounds/angular/src/routes/payment/payment.component.ts create mode 100644 playgrounds/angular/src/routes/todos/todos.component.ts create mode 100644 playgrounds/angular/tsconfig.app.json create mode 100644 playgrounds/angular/tsconfig.json create mode 100644 playgrounds/angular/vite.config.ts diff --git a/playgrounds/angular/index.html b/playgrounds/angular/index.html new file mode 100644 index 00000000..ffa18a1e --- /dev/null +++ b/playgrounds/angular/index.html @@ -0,0 +1,17 @@ + + + + + + + Angular Playground | Formisch + + + + + + diff --git a/playgrounds/angular/package.json b/playgrounds/angular/package.json new file mode 100644 index 00000000..380b3226 --- /dev/null +++ b/playgrounds/angular/package.json @@ -0,0 +1,31 @@ +{ + "name": "formisch-playground-angular", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@angular/common": "^19.0.0", + "@angular/compiler": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/platform-browser": "^19.0.0", + "@angular/router": "^19.0.0", + "@formisch/angular": "workspace:*", + "@tailwindcss/vite": "^4.1.17", + "clsx": "^2.1.1", + "valibot": "^1.2.0" + }, + "devDependencies": { + "@analogjs/vite-plugin-angular": "^1.10.0", + "@angular/build": "^19.0.0", + "@angular/compiler-cli": "^19.0.0", + "@types/node": "^24.10.1", + "tailwindcss": "^4.1.17", + "typescript": "~5.8.3", + "vite": "^7.2.4" + } +} diff --git a/playgrounds/angular/src/app.component.ts b/playgrounds/angular/src/app.component.ts new file mode 100644 index 00000000..1e673d7c --- /dev/null +++ b/playgrounds/angular/src/app.component.ts @@ -0,0 +1,33 @@ +import { Component } from '@angular/core'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [RouterOutlet, RouterLink, RouterLinkActive], + template: ` +
+
+ +
+
+
+ +
+ `, +}) +export class AppComponent { + readonly tabs = [ + { path: '/login', label: 'Login' }, + { path: '/payment', label: 'Payment' }, + { path: '/todos', label: 'Todos' }, + ]; +} diff --git a/playgrounds/angular/src/app.config.ts b/playgrounds/angular/src/app.config.ts new file mode 100644 index 00000000..296f5d4f --- /dev/null +++ b/playgrounds/angular/src/app.config.ts @@ -0,0 +1,10 @@ +import { provideExperimentalZonelessChangeDetection, type ApplicationConfig } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { routes } from './app.routes.ts'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideExperimentalZonelessChangeDetection(), + provideRouter(routes), + ], +}; diff --git a/playgrounds/angular/src/app.routes.ts b/playgrounds/angular/src/app.routes.ts new file mode 100644 index 00000000..4b13f41d --- /dev/null +++ b/playgrounds/angular/src/app.routes.ts @@ -0,0 +1,26 @@ +import type { Routes } from '@angular/router'; + +export const routes: Routes = [ + { path: '', redirectTo: 'login', pathMatch: 'full' }, + { + path: 'login', + loadComponent: () => + import('./routes/login/login.component.ts').then( + (m) => m.LoginComponent + ), + }, + { + path: 'payment', + loadComponent: () => + import('./routes/payment/payment.component.ts').then( + (m) => m.PaymentComponent + ), + }, + { + path: 'todos', + loadComponent: () => + import('./routes/todos/todos.component.ts').then( + (m) => m.TodosComponent + ), + }, +]; diff --git a/playgrounds/angular/src/components/form-footer.component.ts b/playgrounds/angular/src/components/form-footer.component.ts new file mode 100644 index 00000000..fcdd7202 --- /dev/null +++ b/playgrounds/angular/src/components/form-footer.component.ts @@ -0,0 +1,31 @@ +import { Component, input } from '@angular/core'; +import { reset, type FormStore } from '@formisch/angular'; + +@Component({ + selector: 'app-form-footer', + standalone: true, + template: ` +
+ + +
+ `, +}) +export class FormFooterComponent { + readonly of = input.required(); + + protected handleReset(): void { + reset(this.of()); + } +} diff --git a/playgrounds/angular/src/components/form-header.component.ts b/playgrounds/angular/src/components/form-header.component.ts new file mode 100644 index 00000000..641bd2f2 --- /dev/null +++ b/playgrounds/angular/src/components/form-header.component.ts @@ -0,0 +1,37 @@ +import { Component, input } from '@angular/core'; +import { reset, type FormStore } from '@formisch/angular'; + +@Component({ + selector: 'app-form-header', + standalone: true, + template: ` +
+

+ {{ heading() }} +

+ +
+ `, +}) +export class FormHeaderComponent { + readonly of = input.required(); + readonly heading = input.required(); + + protected handleReset(): void { + reset(this.of()); + } +} diff --git a/playgrounds/angular/src/components/text-input.component.ts b/playgrounds/angular/src/components/text-input.component.ts new file mode 100644 index 00000000..0b335b17 --- /dev/null +++ b/playgrounds/angular/src/components/text-input.component.ts @@ -0,0 +1,65 @@ +import { Component, computed, input, output } from '@angular/core'; +import clsx from 'clsx'; + +@Component({ + selector: 'app-text-input', + standalone: true, + template: ` +
+ @if (label()) { + + } + + @if (errors()) { +
+ {{ errors()![0] }} +
+ } +
+ `, +}) +export class TextInputComponent { + readonly name = input.required(); + readonly type = input('text'); + readonly label = input(); + readonly placeholder = input(); + readonly required = input(false); + readonly value = input(); + readonly errors = input<[string, ...string[]] | null>(null); + + readonly fieldFocus = output(); + readonly fieldChange = output(); + readonly fieldBlur = output(); + + protected readonly inputClass = computed(() => + clsx( + 'h-14 w-full rounded-2xl border-2 bg-white px-5 outline-none placeholder:text-slate-500 md:h-16 md:text-lg lg:h-[70px] lg:px-6 lg:text-xl dark:bg-gray-900', + this.errors() + ? 'border-red-600/50 dark:border-red-400/50' + : 'border-slate-200 hover:border-slate-300 focus:border-sky-600/50 dark:border-slate-800 dark:hover:border-slate-700 dark:focus:border-sky-400/50' + ) + ); +} diff --git a/playgrounds/angular/src/global.css b/playgrounds/angular/src/global.css new file mode 100644 index 00000000..6ad1ba7e --- /dev/null +++ b/playgrounds/angular/src/global.css @@ -0,0 +1,26 @@ +@import 'tailwindcss'; + +/* Lexend 400 */ +@font-face { + font-family: 'Lexend'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(/fonts/lexend-400.woff2) format('woff2'); +} + +/* Base styles */ +html { + font-family: 'Lexend', ui-sans-serif, system-ui, sans-serif; +} + +/* Disable-transitions */ +.disable-transitions, +.disable-transitions * { + transition: none !important; +} + +/* Reset appearance */ +[type='date'] { + @apply flex appearance-none items-center justify-start; +} diff --git a/playgrounds/angular/src/main.ts b/playgrounds/angular/src/main.ts new file mode 100644 index 00000000..76b90249 --- /dev/null +++ b/playgrounds/angular/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app.component.ts'; +import { appConfig } from './app.config.ts'; +import './global.css'; + +bootstrapApplication(AppComponent, appConfig).catch(console.error); diff --git a/playgrounds/angular/src/routes/login/login.component.ts b/playgrounds/angular/src/routes/login/login.component.ts new file mode 100644 index 00000000..f882261f --- /dev/null +++ b/playgrounds/angular/src/routes/login/login.component.ts @@ -0,0 +1,79 @@ +import { Component } from '@angular/core'; +import * as v from 'valibot'; +import { FormischForm, FormischField, injectForm } from '@formisch/angular'; +import { FormHeaderComponent } from '../../components/form-header.component.ts'; +import { FormFooterComponent } from '../../components/form-footer.component.ts'; +import { TextInputComponent } from '../../components/text-input.component.ts'; + +const LoginSchema = v.object({ + email: v.pipe( + v.string('Please enter your email.'), + v.nonEmpty('Please enter your email.'), + v.email('The email address is badly formatted.') + ), + password: v.pipe( + v.string('Please enter your password.'), + v.nonEmpty('Please enter your password.'), + v.minLength(8, 'Your password must have 8 characters or more.') + ), +}); + +@Component({ + selector: 'app-login', + standalone: true, + imports: [FormischForm, FormischField, FormHeaderComponent, FormFooterComponent, TextInputComponent], + template: ` + + + +
+ + + + + + + + + + + +
+ + +
+ `, +}) +export class LoginComponent { + readonly form = injectForm({ schema: LoginSchema, validate: 'blur' }); + + readonly handleSubmit = (output: v.InferOutput) => { + console.log(output); + }; +} diff --git a/playgrounds/angular/src/routes/payment/payment.component.ts b/playgrounds/angular/src/routes/payment/payment.component.ts new file mode 100644 index 00000000..d43e4443 --- /dev/null +++ b/playgrounds/angular/src/routes/payment/payment.component.ts @@ -0,0 +1,181 @@ +import { Component, computed } from '@angular/core'; +import * as v from 'valibot'; +import { FormischForm, FormischField, getInput, injectForm } from '@formisch/angular'; +import { FormHeaderComponent } from '../../components/form-header.component.ts'; +import { FormFooterComponent } from '../../components/form-footer.component.ts'; +import { TextInputComponent } from '../../components/text-input.component.ts'; + +const PaymentSchema = v.intersect([ + v.object({ + owner: v.pipe( + v.string('Please enter your name.'), + v.nonEmpty('Please enter your name.') + ), + }), + v.variant('type', [ + v.object({ + type: v.literal('card'), + card: v.object({ + number: v.pipe( + v.string('Please enter your card number.'), + v.nonEmpty('Please enter your card number.'), + v.creditCard('The card number is badly formatted.') + ), + expiration: v.pipe( + v.string('Please enter the expiration date.'), + v.nonEmpty('Please enter the expiration date.'), + v.regex( + /^(?:0[1-9]|1[0-2])\/(?:2[5-9]|3[0-9])$/, + 'The expiration date is badly formatted.' + ) + ), + }), + }), + v.object({ + type: v.literal('paypal'), + paypal: v.object({ + email: v.pipe( + v.string('Please enter your PayPal email.'), + v.nonEmpty('Please enter your PayPal email.'), + v.email('The email address is badly formatted.') + ), + }), + }), + ], 'Please select the payment type.'), +]); + +@Component({ + selector: 'app-payment', + standalone: true, + imports: [FormischForm, FormischField, FormHeaderComponent, FormFooterComponent, TextInputComponent], + template: ` + + + +
+ + + + + + + + +
+ +
+ @for (option of paymentTypes; track option.value) { + + } +
+ @if (field.errors()) { +
+ {{ field.errors()![0] }} +
+ } +
+
+
+ + @if (paymentType() === 'card') { + + + + + + + + + + + + } + + @if (paymentType() === 'paypal') { + + + + + + } +
+ + +
+ `, +}) +export class PaymentComponent { + readonly form = injectForm({ schema: PaymentSchema, validate: 'blur' }); + + readonly paymentTypes = [ + { value: 'card', label: 'Card' }, + { value: 'paypal', label: 'PayPal' }, + ]; + + readonly paymentType = computed(() => + getInput(this.form, { path: ['type'] }) + ); + + readonly handleSubmit = (output: v.InferOutput) => { + console.log(output); + }; +} diff --git a/playgrounds/angular/src/routes/todos/todos.component.ts b/playgrounds/angular/src/routes/todos/todos.component.ts new file mode 100644 index 00000000..adb1ff88 --- /dev/null +++ b/playgrounds/angular/src/routes/todos/todos.component.ts @@ -0,0 +1,172 @@ +import { Component } from '@angular/core'; +import * as v from 'valibot'; +import { + FormischForm, + FormischField, + FormischFieldArray, + insert, + remove, + injectForm, +} from '@formisch/angular'; +import { FormHeaderComponent } from '../../components/form-header.component.ts'; +import { FormFooterComponent } from '../../components/form-footer.component.ts'; +import { TextInputComponent } from '../../components/text-input.component.ts'; + +const TodosSchema = v.object({ + heading: v.pipe( + v.string('Please enter a heading.'), + v.nonEmpty('Please enter a heading.') + ), + todos: v.pipe( + v.array( + v.object({ + label: v.pipe( + v.string('Please enter a label.'), + v.nonEmpty('Please enter a label.') + ), + deadline: v.pipe( + v.string('Please enter a deadline.'), + v.nonEmpty('Please enter a deadline.') + ), + }) + ), + v.nonEmpty('Please add at least one todo.'), + v.maxLength(4, 'You can only add up to 4 todos.') + ), +}); + +@Component({ + selector: 'app-todos', + standalone: true, + imports: [ + FormischForm, + FormischField, + FormischFieldArray, + FormHeaderComponent, + FormFooterComponent, + TextInputComponent, + ], + template: ` + + + +
+ + + + + + + + +
+ + + @if (fieldArray.errors()) { +
+ {{ fieldArray.errors()![0] }} +
+ } + + @for (item of fieldArray.items(); track item; let i = $index) { +
+
+ + + + + + + + + + + +
+ + +
+ } + + +
+
+
+
+ + +
+ `, +}) +export class TodosComponent { + readonly form = injectForm({ + schema: TodosSchema, + validate: 'blur', + initialInput: { + heading: '', + todos: [{ label: '', deadline: '' }], + }, + }); + + readonly handleSubmit = (output: v.InferOutput) => { + console.log(output); + }; + + handleInsert(): void { + insert(this.form, { path: ['todos'], initialInput: { label: '', deadline: '' } }); + } + + handleRemove(index: number): void { + remove(this.form, { path: ['todos'], at: index }); + } +} diff --git a/playgrounds/angular/tsconfig.app.json b/playgrounds/angular/tsconfig.app.json new file mode 100644 index 00000000..42648a69 --- /dev/null +++ b/playgrounds/angular/tsconfig.app.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": false, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + /* Angular */ + "experimentalDecorators": true, + "emitDecoratorMetadata": false, + "strictPropertyInitialization": false, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "paths": { + "@formisch/angular": ["../../frameworks/angular/src/index.ts"] + } + }, + "include": ["src"] +} diff --git a/playgrounds/angular/tsconfig.json b/playgrounds/angular/tsconfig.json new file mode 100644 index 00000000..426eda2b --- /dev/null +++ b/playgrounds/angular/tsconfig.json @@ -0,0 +1,6 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" } + ] +} diff --git a/playgrounds/angular/vite.config.ts b/playgrounds/angular/vite.config.ts new file mode 100644 index 00000000..148d9f23 --- /dev/null +++ b/playgrounds/angular/vite.config.ts @@ -0,0 +1,15 @@ +import tailwindcss from '@tailwindcss/vite'; +import angular from '@analogjs/vite-plugin-angular'; +import { defineConfig } from 'vite'; +import { fileURLToPath, URL } from 'node:url'; + +export default defineConfig({ + plugins: [angular(), tailwindcss()], + resolve: { + alias: { + '@formisch/angular': fileURLToPath( + new URL('../../frameworks/angular/src/index.ts', import.meta.url) + ), + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75710d8f..c36561bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,10 +99,10 @@ importers: version: link:../../packages/methods '@preact/preset-vite': specifier: ^2.10.5 - version: 2.10.5(@babel/core@7.28.5)(preact@10.29.2)(rollup@4.52.5)(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + version: 2.10.5(@babel/core@7.28.5)(preact@10.29.2)(rollup@4.59.0)(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) '@preact/signals': specifier: ^2.9.0 - version: 2.9.0(preact@10.29.2) + version: 2.9.1(preact@10.29.2) '@testing-library/jest-dom': specifier: ^6.6.0 version: 6.9.1 @@ -304,7 +304,7 @@ importers: version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1) tsup-preset-solid: specifier: ^2.2.0 - version: 2.2.0(esbuild@0.25.11)(solid-js@1.9.9)(tsup@8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1)) + version: 2.2.0(esbuild@0.28.0)(solid-js@1.9.9)(tsup@8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1)) typescript: specifier: ^5.8.3 version: 5.8.3 @@ -454,7 +454,7 @@ importers: version: link:../eslint-config '@preact/signals': specifier: ^2.9.0 - version: 2.9.0(preact@10.29.2) + version: 2.9.1(preact@10.29.2) '@qwik.dev/core': specifier: 2.0.0-beta.5 version: 2.0.0-beta.5(prettier@3.6.2)(vite@7.2.6(@types/node@24.0.13)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@24.0.13)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) @@ -568,6 +568,58 @@ importers: specifier: 3.2.4 version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@24.0.13)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + playgrounds/angular: + dependencies: + '@angular/common': + specifier: ^19.0.0 + version: 19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^19.0.0 + version: 19.2.22 + '@angular/core': + specifier: ^19.0.0 + version: 19.2.22(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': + specifier: ^19.0.0 + version: 19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/router': + specifier: ^19.0.0 + version: 19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@formisch/angular': + specifier: workspace:* + version: link:../../frameworks/angular + '@tailwindcss/vite': + specifier: ^4.1.17 + version: 4.1.17(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + clsx: + specifier: ^2.1.1 + version: 2.1.1 + valibot: + specifier: ^1.2.0 + version: 1.2.0(typescript@5.8.3) + devDependencies: + '@analogjs/vite-plugin-angular': + specifier: ^1.10.0 + version: 1.22.5(@angular/build@19.2.26(@angular/compiler-cli@19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3))(@angular/compiler@19.2.22)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(postcss@8.5.6)(stylus@0.62.0)(tailwindcss@4.1.17)(terser@5.44.0)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1)) + '@angular/build': + specifier: ^19.0.0 + version: 19.2.26(@angular/compiler-cli@19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3))(@angular/compiler@19.2.22)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(postcss@8.5.6)(stylus@0.62.0)(tailwindcss@4.1.17)(terser@5.44.0)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1) + '@angular/compiler-cli': + specifier: ^19.0.0 + version: 19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3) + '@types/node': + specifier: ^24.10.1 + version: 24.10.1 + tailwindcss: + specifier: ^4.1.17 + version: 4.1.17 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + vite: + specifier: ^7.2.4 + version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + playgrounds/preact: dependencies: '@formisch/preact': @@ -578,7 +630,7 @@ importers: version: 0.8.4 '@preact/signals': specifier: ^2.9.0 - version: 2.9.0(preact@10.29.2) + version: 2.9.1(preact@10.29.2) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -597,7 +649,7 @@ importers: version: link:../../packages/eslint-config '@preact/preset-vite': specifier: ^2.10.5 - version: 2.10.5(@babel/core@7.28.5)(preact@10.29.2)(rollup@4.52.5)(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + version: 2.10.5(@babel/core@7.28.5)(preact@10.29.2)(rollup@4.59.0)(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) '@tailwindcss/vite': specifier: ^4.1.11 version: 4.1.16(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) @@ -847,7 +899,7 @@ importers: version: 5.42.1 svelte-check: specifier: ^4.3.1 - version: 4.3.3(picomatch@4.0.3)(svelte@5.42.1)(typescript@5.9.3) + version: 4.3.3(picomatch@4.0.4)(svelte@5.42.1)(typescript@5.9.3) tailwindcss: specifier: ^4.1.13 version: 4.1.16 @@ -957,7 +1009,7 @@ importers: version: 2.0.0-beta.9(prettier@3.6.2)(vite@5.4.21(@types/node@22.18.12)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0))(vitest@3.2.4(@edge-runtime/vm@3.2.0)(@types/debug@4.1.12)(@types/node@22.18.12)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) '@qwik.dev/router': specifier: 2.0.0-beta.9 - version: 2.0.0-beta.9(patch_hash=e002def78f4681060d18bc24d686c24ee1d1b997c399a2cb1c03baf3b860e4c8)(rollup@4.52.5)(typescript@5.8.3) + version: 2.0.0-beta.9(patch_hash=e002def78f4681060d18bc24d686c24ee1d1b997c399a2cb1c03baf3b860e4c8)(rollup@4.59.0)(typescript@5.8.3) '@shikijs/rehype': specifier: ^3.13.0 version: 3.13.0 @@ -1029,7 +1081,7 @@ importers: version: 5.4.21(@types/node@22.18.12)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0) vite-imagetools: specifier: ^7.0.5 - version: 7.1.1(rollup@4.52.5) + version: 7.1.1(rollup@4.59.0) vite-tsconfig-paths: specifier: ^5.1.4 version: 5.1.4(typescript@5.8.3)(vite@5.4.21(@types/node@22.18.12)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)) @@ -1046,6 +1098,66 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@analogjs/vite-plugin-angular@1.22.5': + resolution: {integrity: sha512-N1BQD6HQSp2Imbb1fThymskWFSLq0ZF+d2fe3DgErwlBFf6SzRp++iFltddQc3wIzenTXE+5brS4fAPXO8UT9g==} + peerDependencies: + '@angular-devkit/build-angular': ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 + '@angular/build': ^18.0.0 || ^19.0.0 || ^20.0.0 + peerDependenciesMeta: + '@angular-devkit/build-angular': + optional: true + '@angular/build': + optional: true + + '@angular-devkit/architect@0.1902.26': + resolution: {integrity: sha512-YL0SE4B+zLkPJa7glWBqNJzZS2qMMq8GzuGACJvHnRETL3jad34q3ByIM+wOTXbAVbKef3Poa/sPPunjMLdZUA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@angular-devkit/core@19.2.26': + resolution: {integrity: sha512-50HrZQ2S/t9bWDEg30qqkwHUCH+k7pmslPqHLMFe0q+oUNaL5Ru+fi/L84bDzqRP/0mkeIIY2rOuUWrw6ZpGvQ==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + + '@angular/build@19.2.26': + resolution: {integrity: sha512-pcV/m+hm5dD05d2xAOjr5dIdSJoSZDmM1OKbD551196OXiXb88F52hw0Rmn+TWk1+ksX3ivjgH0lSe7ww65mPA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + '@angular/compiler': ^19.0.0 || ^19.2.0-next.0 + '@angular/compiler-cli': ^19.0.0 || ^19.2.0-next.0 + '@angular/localize': ^19.0.0 || ^19.2.0-next.0 + '@angular/platform-server': ^19.0.0 || ^19.2.0-next.0 + '@angular/service-worker': ^19.0.0 || ^19.2.0-next.0 + '@angular/ssr': ^19.2.26 + karma: ^6.4.0 + less: ^4.2.0 + ng-packagr: ^19.0.0 || ^19.2.0-next.0 + postcss: ^8.4.0 + tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 + typescript: '>=5.5 <5.9' + peerDependenciesMeta: + '@angular/localize': + optional: true + '@angular/platform-server': + optional: true + '@angular/service-worker': + optional: true + '@angular/ssr': + optional: true + karma: + optional: true + less: + optional: true + ng-packagr: + optional: true + postcss: + optional: true + tailwindcss: + optional: true + '@angular/common@19.2.22': resolution: {integrity: sha512-OGvJkK9xvlXF0LsM8T2NTgL6ehr+P4t493xztdIwiO7CzseDzHCBNZ8L1TwaVqqP3Xj0UhGTtWXp7YN4N9+u7Q==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} @@ -1053,6 +1165,14 @@ packages: '@angular/core': 19.2.22 rxjs: ^6.5.3 || ^7.4.0 + '@angular/compiler-cli@19.2.22': + resolution: {integrity: sha512-zwl4gh5smX95AduH60RpeToKRpRtj2oVEJ3KsgCb6KUfLbe/PK3lCQaLVW4tp1BbiCvCC05lhYC7ARtMbFFHrA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + hasBin: true + peerDependencies: + '@angular/compiler': 19.2.22 + typescript: '>=5.5 <5.9' + '@angular/compiler@19.2.22': resolution: {integrity: sha512-NO3q39TMq88MRyBM9E/cSn4KFEItPrp79izX3nRkWlGQ0D9nJPdzm2cHtZoB9dCZ3N6EOazTgBK+2jKylQrUFQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} @@ -1088,6 +1208,15 @@ packages: '@angular/animations': optional: true + '@angular/router@19.2.22': + resolution: {integrity: sha512-FNxNJ2WifZdcOzieJHHugNTZWgZArD35bnmpO3m7V9Bow43Hy42xAOpHvAGMw8wZgVFBQl6C+Wr6GZB3eQzX/w==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/common': 19.2.22 + '@angular/core': 19.2.22 + '@angular/platform-browser': 19.2.22 + rxjs: ^6.5.3 || ^7.4.0 + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -1103,6 +1232,14 @@ packages: resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} + '@babel/core@7.26.10': + resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.9': + resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} @@ -1118,6 +1255,10 @@ packages: resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.25.9': + resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -1189,6 +1330,10 @@ packages: resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -1273,6 +1418,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-attributes@7.27.1': resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} @@ -1774,6 +1925,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.0': + resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.17.19': resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} @@ -1798,6 +1955,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.0': + resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.15.18': resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} engines: {node: '>=12'} @@ -1828,6 +1991,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.0': + resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.17.19': resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} engines: {node: '>=12'} @@ -1852,6 +2021,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.0': + resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.17.19': resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} engines: {node: '>=12'} @@ -1876,6 +2051,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.0': + resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.17.19': resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} engines: {node: '>=12'} @@ -1900,6 +2081,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.0': + resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.17.19': resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} engines: {node: '>=12'} @@ -1924,6 +2111,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.0': + resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.17.19': resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} engines: {node: '>=12'} @@ -1948,6 +2141,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.0': + resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.17.19': resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} engines: {node: '>=12'} @@ -1972,6 +2171,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.0': + resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.17.19': resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} engines: {node: '>=12'} @@ -1996,6 +2201,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.0': + resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.17.19': resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} engines: {node: '>=12'} @@ -2020,6 +2231,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.0': + resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.15.18': resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} engines: {node: '>=12'} @@ -2050,6 +2267,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.0': + resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.17.19': resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} engines: {node: '>=12'} @@ -2074,6 +2297,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.0': + resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.17.19': resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} engines: {node: '>=12'} @@ -2098,6 +2327,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.0': + resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.17.19': resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} engines: {node: '>=12'} @@ -2122,6 +2357,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.0': + resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.17.19': resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} engines: {node: '>=12'} @@ -2146,6 +2387,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.0': + resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.17.19': resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} engines: {node: '>=12'} @@ -2170,12 +2417,24 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.0': + resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.11': resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.0': + resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.17.19': resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} engines: {node: '>=12'} @@ -2200,12 +2459,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.0': + resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.11': resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.0': + resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.17.19': resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} engines: {node: '>=12'} @@ -2230,12 +2501,24 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.0': + resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.11': resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.0': + resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.17.19': resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} engines: {node: '>=12'} @@ -2260,6 +2543,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.0': + resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.17.19': resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} engines: {node: '>=12'} @@ -2284,6 +2573,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.0': + resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.17.19': resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} engines: {node: '>=12'} @@ -2308,6 +2603,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.0': + resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.17.19': resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} engines: {node: '>=12'} @@ -2332,6 +2633,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.0': + resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.0': resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2684,6 +2991,28 @@ packages: cpu: [x64] os: [win32] + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} + engines: {node: '>=18'} + + '@inquirer/confirm@5.1.6': + resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/external-editor@1.0.2': resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==} engines: {node: '>=18'} @@ -2693,6 +3022,19 @@ packages: '@types/node': optional: true + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + engines: {node: '>=18'} + + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@ioredis/commands@1.4.0': resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==} @@ -2733,6 +3075,36 @@ packages: '@jspm/core@2.1.0': resolution: {integrity: sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==} + '@lmdb/lmdb-darwin-arm64@3.2.6': + resolution: {integrity: sha512-yF/ih9EJJZc72psFQbwnn8mExIWfTnzWJg+N02hnpXtDPETYLmQswIMBn7+V88lfCaFrMozJsUvcEQIkEPU0Gg==} + cpu: [arm64] + os: [darwin] + + '@lmdb/lmdb-darwin-x64@3.2.6': + resolution: {integrity: sha512-5BbCumsFLbCi586Bb1lTWQFkekdQUw8/t8cy++Uq251cl3hbDIGEwD9HAwh8H6IS2F6QA9KdKmO136LmipRNkg==} + cpu: [x64] + os: [darwin] + + '@lmdb/lmdb-linux-arm64@3.2.6': + resolution: {integrity: sha512-l5VmJamJ3nyMmeD1ANBQCQqy7do1ESaJQfKPSm2IG9/ADZryptTyCj8N6QaYgIWewqNUrcbdMkJajRQAt5Qjfg==} + cpu: [arm64] + os: [linux] + + '@lmdb/lmdb-linux-arm@3.2.6': + resolution: {integrity: sha512-+6XgLpMb7HBoWxXj+bLbiiB4s0mRRcDPElnRS3LpWRzdYSe+gFk5MT/4RrVNqd2MESUDmb53NUXw1+BP69bjiQ==} + cpu: [arm] + os: [linux] + + '@lmdb/lmdb-linux-x64@3.2.6': + resolution: {integrity: sha512-nDYT8qN9si5+onHYYaI4DiauDMx24OAiuZAUsEqrDy+ja/3EbpXPX/VAkMV8AEaQhy3xc4dRC+KcYIvOFefJ4Q==} + cpu: [x64] + os: [linux] + + '@lmdb/lmdb-win32-x64@3.2.6': + resolution: {integrity: sha512-XlqVtILonQnG+9fH2N3Aytria7P/1fwDgDhl29rde96uH2sLB8CHORIf2PfuLVzFQJ7Uqp8py9AYwr3ZUCFfWg==} + cpu: [x64] + os: [win32] + '@mapbox/node-pre-gyp@1.0.11': resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} hasBin: true @@ -2748,25 +3120,161 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - '@napi-rs/wasm-runtime@0.2.12': - resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] - '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': - resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@napi-rs/nice-android-arm-eabi@1.1.1': + resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/nice-android-arm64@1.1.1': + resolution: {integrity: sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/nice-darwin-arm64@1.1.1': + resolution: {integrity: sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/nice-darwin-x64@1.1.1': + resolution: {integrity: sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/nice-freebsd-x64@1.1.1': + resolution: {integrity: sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': + resolution: {integrity: sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/nice-linux-arm64-gnu@1.1.1': + resolution: {integrity: sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@napi-rs/nice-linux-arm64-musl@1.1.1': + resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@napi-rs/nice-linux-ppc64-gnu@1.1.1': + resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} + engines: {node: '>= 10'} + cpu: [ppc64] + os: [linux] + + '@napi-rs/nice-linux-riscv64-gnu@1.1.1': + resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@napi-rs/nice-linux-s390x-gnu@1.1.1': + resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} + engines: {node: '>= 10'} + cpu: [s390x] + os: [linux] + + '@napi-rs/nice-linux-x64-gnu@1.1.1': + resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@napi-rs/nice-linux-x64-musl@1.1.1': + resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@napi-rs/nice-openharmony-arm64@1.1.1': + resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [openharmony] + + '@napi-rs/nice-win32-arm64-msvc@1.1.1': + resolution: {integrity: sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/nice-win32-ia32-msvc@1.1.1': + resolution: {integrity: sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/nice-win32-x64-msvc@1.1.1': + resolution: {integrity: sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/nice@1.1.1': + resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} + engines: {node: '>= 10'} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': + resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} '@npmcli/fs@1.1.1': @@ -2975,8 +3483,8 @@ packages: '@preact/signals-core@1.14.2': resolution: {integrity: sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A==} - '@preact/signals@2.9.0': - resolution: {integrity: sha512-hYrY0KyUqkDgOl1qba/JGn6y81pXnurn21PMaxfcMwdncdZ3M/oVdmpTvEnsGjh48dIwDVc7bjWHqIsngSjYug==} + '@preact/signals@2.9.1': + resolution: {integrity: sha512-xVqN8mJjbSN5IB/8Ubmd9NN+Ew6zJswoRxrjZbH3YsgkMshFeO6d8zxEFpHRTq9GJZx7cnPs2CnCpFqtGXGNsw==} peerDependencies: preact: '>= 10.25.0 || >=11.0.0-0' @@ -3303,111 +3811,236 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.52.5': resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.52.5': resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.52.5': resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.52.5': resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.52.5': resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.52.5': resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.52.5': resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.52.5': resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loong64-gnu@4.52.5': resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.52.5': resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.52.5': resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.52.5': resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.52.5': resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.52.5': resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.52.5': resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + cpu: [x64] + os: [openbsd] + '@rollup/rollup-openharmony-arm64@4.52.5': resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} cpu: [arm64] os: [openharmony] + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.52.5': resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.52.5': resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-gnu@4.52.5': resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.52.5': resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + cpu: [x64] + os: [win32] + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -3836,6 +4469,9 @@ packages: '@ts-morph/common@0.11.1': resolution: {integrity: sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==} + '@ts-morph/common@0.22.0': + resolution: {integrity: sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -4264,6 +4900,12 @@ packages: peerDependencies: vinxi: ^0.5.5 + '@vitejs/plugin-basic-ssl@1.2.0': + resolution: {integrity: sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + '@vitejs/plugin-react@5.1.1': resolution: {integrity: sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4485,9 +5127,20 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.6.3: resolution: {integrity: sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==} @@ -4501,6 +5154,10 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -4746,6 +5403,10 @@ packages: resolution: {integrity: sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==} hasBin: true + beasties@0.3.2: + resolution: {integrity: sha512-p4AF8uYzm9Fwu8m/hSVTCPXrRBPmB34hQpHsec2KOaR9CZmgoU8IOv4Cvwq4hgz2p4hLMNbsdNl5XeA6XbAQwA==} + engines: {node: '>=14.0.0'} + before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} @@ -4946,14 +5607,26 @@ packages: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + cli-width@3.0.0: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + clipboardy@4.0.0: resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} engines: {node: '>=18'} @@ -4980,6 +5653,9 @@ packages: code-block-writer@10.1.1: resolution: {integrity: sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==} + code-block-writer@12.0.0: + resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -5071,6 +5747,9 @@ packages: resolution: {integrity: sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==} engines: {node: '>=8'} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -5535,6 +6214,14 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -5871,6 +6558,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.0: + resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -6170,6 +6862,9 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + events-universal@1.0.1: resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} @@ -6230,6 +6925,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -6448,6 +7146,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -6618,6 +7319,9 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -6857,6 +7561,14 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -7020,6 +7732,10 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} @@ -7134,6 +7850,9 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -7279,6 +7998,14 @@ packages: resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==} hasBin: true + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + engines: {node: '>=18.0.0'} + + lmdb@3.2.6: + resolution: {integrity: sha512-SuHqzPl7mYStna8WRotY8XX/EUZBjjv3QyKIByeCLFfC9uXT/OIHByEcA07PzbMfQAM0KYJtLgtpMRlIe5dErQ==} + hasBin: true + load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7330,6 +8057,10 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -7362,6 +8093,9 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -7714,6 +8448,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -7783,6 +8521,11 @@ packages: engines: {node: '>=10'} hasBin: true + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -7803,12 +8546,23 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.12: + resolution: {integrity: sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==} + muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -7851,6 +8605,9 @@ packages: node-addon-api@1.7.2: resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} @@ -7879,6 +8636,10 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -8007,6 +8768,10 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} @@ -8036,6 +8801,9 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + ordered-binary@1.6.1: + resolution: {integrity: sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==} + outdent@0.8.0: resolution: {integrity: sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==} @@ -8100,6 +8868,12 @@ packages: parse-statements@1.0.11: resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + parse5-html-rewriting-stream@7.0.0: + resolution: {integrity: sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==} + + parse5-sax-parser@7.0.0: + resolution: {integrity: sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -8186,6 +8960,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} engines: {node: '>=0.10'} @@ -8199,6 +8977,9 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + piscina@4.8.0: + resolution: {integrity: sha512-EZJb+ZxDrQf3dihsUL7p42pjNyrNIFJCrRHPMgxu/svsj+P3xS3fuEWp7k2+rfsavfl1N0G29b1HGs7J0m8rZA==} + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -8257,6 +9038,9 @@ packages: yaml: optional: true + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + postcss-modules-extract-imports@3.1.0: resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} engines: {node: ^10 || ^12 || >= 14} @@ -8625,6 +9409,9 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -8748,6 +9535,10 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -8825,6 +9616,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -8839,6 +9635,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -8870,6 +9669,11 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sass@1.85.0: + resolution: {integrity: sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==} + engines: {node: '>=14.0.0'} + hasBin: true + sass@1.93.2: resolution: {integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==} engines: {node: '>=14.0.0'} @@ -8919,6 +9723,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -9047,6 +9856,14 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -9094,6 +9911,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + source-map@0.7.6: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} @@ -9480,6 +10301,9 @@ packages: ts-morph@12.0.0: resolution: {integrity: sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==} + ts-morph@21.0.1: + resolution: {integrity: sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==} + ts-node@10.9.1: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -9950,7 +10774,15 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - valibot@1.4.1: + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + + valibot@1.4.1: resolution: {integrity: sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==} peerDependencies: typescript: '>=5' @@ -10128,6 +10960,46 @@ packages: yaml: optional: true + vite@6.4.2: + resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + 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 + vite@7.0.4: resolution: {integrity: sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -10324,9 +11196,16 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + watchpack@2.4.2: + resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} + engines: {node: '>=10.13.0'} + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + weak-lru-cache@1.2.2: + resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} + web-vitals@0.2.4: resolution: {integrity: sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==} @@ -10527,6 +11406,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + yoga-wasm-web@0.3.3: resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} @@ -10576,12 +11459,101 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 + '@analogjs/vite-plugin-angular@1.22.5(@angular/build@19.2.26(@angular/compiler-cli@19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3))(@angular/compiler@19.2.22)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(postcss@8.5.6)(stylus@0.62.0)(tailwindcss@4.1.17)(terser@5.44.0)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1))': + dependencies: + ts-morph: 21.0.1 + vfile: 6.0.3 + optionalDependencies: + '@angular/build': 19.2.26(@angular/compiler-cli@19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3))(@angular/compiler@19.2.22)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(postcss@8.5.6)(stylus@0.62.0)(tailwindcss@4.1.17)(terser@5.44.0)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1) + + '@angular-devkit/architect@0.1902.26(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.26(chokidar@4.0.3) + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + + '@angular-devkit/core@19.2.26(chokidar@4.0.3)': + dependencies: + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + jsonc-parser: 3.3.1 + picomatch: 4.0.4 + rxjs: 7.8.1 + source-map: 0.7.4 + optionalDependencies: + chokidar: 4.0.3 + + '@angular/build@19.2.26(@angular/compiler-cli@19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3))(@angular/compiler@19.2.22)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(postcss@8.5.6)(stylus@0.62.0)(tailwindcss@4.1.17)(terser@5.44.0)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1)': + dependencies: + '@ampproject/remapping': 2.3.0 + '@angular-devkit/architect': 0.1902.26(chokidar@4.0.3) + '@angular/compiler': 19.2.22 + '@angular/compiler-cli': 19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3) + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) + '@inquirer/confirm': 5.1.6(@types/node@24.10.1) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.4.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + beasties: 0.3.2 + browserslist: 4.27.0 + esbuild: 0.28.0 + fast-glob: 3.3.3 + https-proxy-agent: 7.0.6 + istanbul-lib-instrument: 6.0.3 + listr2: 8.2.5 + magic-string: 0.30.17 + mrmime: 2.0.1 + parse5-html-rewriting-stream: 7.0.0 + picomatch: 4.0.4 + piscina: 4.8.0 + rollup: 4.59.0 + sass: 1.85.0 + semver: 7.7.1 + source-map-support: 0.5.21 + typescript: 5.8.3 + vite: 6.4.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + watchpack: 2.4.2 + optionalDependencies: + less: 4.4.2 + lmdb: 3.2.6 + postcss: 8.5.6 + tailwindcss: 4.1.17 + transitivePeerDependencies: + - '@types/node' + - chokidar + - jiti + - lightningcss + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + '@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: '@angular/core': 19.2.22(rxjs@7.8.2)(zone.js@0.15.1) rxjs: 7.8.2 tslib: 2.8.1 + '@angular/compiler-cli@19.2.22(@angular/compiler@19.2.22)(typescript@5.8.3)': + dependencies: + '@angular/compiler': 19.2.22 + '@babel/core': 7.26.9 + '@jridgewell/sourcemap-codec': 1.5.5 + chokidar: 4.0.3 + convert-source-map: 1.9.0 + reflect-metadata: 0.2.2 + semver: 7.7.3 + tslib: 2.8.1 + typescript: 5.8.3 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + '@angular/compiler@19.2.22': dependencies: tslib: 2.8.1 @@ -10605,6 +11577,14 @@ snapshots: '@angular/core': 19.2.22(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 + '@angular/router@19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + dependencies: + '@angular/common': 19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.22(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 19.2.22(@angular/common@19.2.22(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.22(rxjs@7.8.2)(zone.js@0.15.1)) + rxjs: 7.8.2 + tslib: 2.8.1 + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -10627,6 +11607,46 @@ snapshots: '@babel/compat-data@7.28.5': {} + '@babel/core@7.26.10': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.26.10) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.26.9': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.26.9) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -10663,6 +11683,10 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.25.9': + dependencies: + '@babel/types': 7.28.5 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.28.5 @@ -10726,6 +11750,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -10766,6 +11808,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.28.5 + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} @@ -10852,6 +11898,11 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -11464,6 +12515,9 @@ snapshots: '@esbuild/aix-ppc64@0.25.11': optional: true + '@esbuild/aix-ppc64@0.28.0': + optional: true + '@esbuild/android-arm64@0.17.19': optional: true @@ -11476,6 +12530,9 @@ snapshots: '@esbuild/android-arm64@0.25.11': optional: true + '@esbuild/android-arm64@0.28.0': + optional: true + '@esbuild/android-arm@0.15.18': optional: true @@ -11491,6 +12548,9 @@ snapshots: '@esbuild/android-arm@0.25.11': optional: true + '@esbuild/android-arm@0.28.0': + optional: true + '@esbuild/android-x64@0.17.19': optional: true @@ -11503,6 +12563,9 @@ snapshots: '@esbuild/android-x64@0.25.11': optional: true + '@esbuild/android-x64@0.28.0': + optional: true + '@esbuild/darwin-arm64@0.17.19': optional: true @@ -11515,6 +12578,9 @@ snapshots: '@esbuild/darwin-arm64@0.25.11': optional: true + '@esbuild/darwin-arm64@0.28.0': + optional: true + '@esbuild/darwin-x64@0.17.19': optional: true @@ -11527,6 +12593,9 @@ snapshots: '@esbuild/darwin-x64@0.25.11': optional: true + '@esbuild/darwin-x64@0.28.0': + optional: true + '@esbuild/freebsd-arm64@0.17.19': optional: true @@ -11539,6 +12608,9 @@ snapshots: '@esbuild/freebsd-arm64@0.25.11': optional: true + '@esbuild/freebsd-arm64@0.28.0': + optional: true + '@esbuild/freebsd-x64@0.17.19': optional: true @@ -11551,6 +12623,9 @@ snapshots: '@esbuild/freebsd-x64@0.25.11': optional: true + '@esbuild/freebsd-x64@0.28.0': + optional: true + '@esbuild/linux-arm64@0.17.19': optional: true @@ -11563,6 +12638,9 @@ snapshots: '@esbuild/linux-arm64@0.25.11': optional: true + '@esbuild/linux-arm64@0.28.0': + optional: true + '@esbuild/linux-arm@0.17.19': optional: true @@ -11575,6 +12653,9 @@ snapshots: '@esbuild/linux-arm@0.25.11': optional: true + '@esbuild/linux-arm@0.28.0': + optional: true + '@esbuild/linux-ia32@0.17.19': optional: true @@ -11587,6 +12668,9 @@ snapshots: '@esbuild/linux-ia32@0.25.11': optional: true + '@esbuild/linux-ia32@0.28.0': + optional: true + '@esbuild/linux-loong64@0.15.18': optional: true @@ -11602,6 +12686,9 @@ snapshots: '@esbuild/linux-loong64@0.25.11': optional: true + '@esbuild/linux-loong64@0.28.0': + optional: true + '@esbuild/linux-mips64el@0.17.19': optional: true @@ -11614,6 +12701,9 @@ snapshots: '@esbuild/linux-mips64el@0.25.11': optional: true + '@esbuild/linux-mips64el@0.28.0': + optional: true + '@esbuild/linux-ppc64@0.17.19': optional: true @@ -11626,6 +12716,9 @@ snapshots: '@esbuild/linux-ppc64@0.25.11': optional: true + '@esbuild/linux-ppc64@0.28.0': + optional: true + '@esbuild/linux-riscv64@0.17.19': optional: true @@ -11638,6 +12731,9 @@ snapshots: '@esbuild/linux-riscv64@0.25.11': optional: true + '@esbuild/linux-riscv64@0.28.0': + optional: true + '@esbuild/linux-s390x@0.17.19': optional: true @@ -11650,6 +12746,9 @@ snapshots: '@esbuild/linux-s390x@0.25.11': optional: true + '@esbuild/linux-s390x@0.28.0': + optional: true + '@esbuild/linux-x64@0.17.19': optional: true @@ -11662,9 +12761,15 @@ snapshots: '@esbuild/linux-x64@0.25.11': optional: true + '@esbuild/linux-x64@0.28.0': + optional: true + '@esbuild/netbsd-arm64@0.25.11': optional: true + '@esbuild/netbsd-arm64@0.28.0': + optional: true + '@esbuild/netbsd-x64@0.17.19': optional: true @@ -11677,9 +12782,15 @@ snapshots: '@esbuild/netbsd-x64@0.25.11': optional: true + '@esbuild/netbsd-x64@0.28.0': + optional: true + '@esbuild/openbsd-arm64@0.25.11': optional: true + '@esbuild/openbsd-arm64@0.28.0': + optional: true + '@esbuild/openbsd-x64@0.17.19': optional: true @@ -11692,9 +12803,15 @@ snapshots: '@esbuild/openbsd-x64@0.25.11': optional: true + '@esbuild/openbsd-x64@0.28.0': + optional: true + '@esbuild/openharmony-arm64@0.25.11': optional: true + '@esbuild/openharmony-arm64@0.28.0': + optional: true + '@esbuild/sunos-x64@0.17.19': optional: true @@ -11707,6 +12824,9 @@ snapshots: '@esbuild/sunos-x64@0.25.11': optional: true + '@esbuild/sunos-x64@0.28.0': + optional: true + '@esbuild/win32-arm64@0.17.19': optional: true @@ -11719,6 +12839,9 @@ snapshots: '@esbuild/win32-arm64@0.25.11': optional: true + '@esbuild/win32-arm64@0.28.0': + optional: true + '@esbuild/win32-ia32@0.17.19': optional: true @@ -11731,6 +12854,9 @@ snapshots: '@esbuild/win32-ia32@0.25.11': optional: true + '@esbuild/win32-ia32@0.28.0': + optional: true + '@esbuild/win32-x64@0.17.19': optional: true @@ -11743,6 +12869,9 @@ snapshots: '@esbuild/win32-x64@0.25.11': optional: true + '@esbuild/win32-x64@0.28.0': + optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': dependencies: eslint: 8.57.1 @@ -12044,6 +13173,28 @@ snapshots: '@img/sharp-win32-x64@0.34.4': optional: true + '@inquirer/ansi@1.0.2': {} + + '@inquirer/confirm@5.1.6(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/core@10.3.2(@types/node@24.10.1)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.10.1) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.1 + '@inquirer/external-editor@1.0.2(@types/node@22.18.12)': dependencies: chardet: 2.1.0 @@ -12051,6 +13202,12 @@ snapshots: optionalDependencies: '@types/node': 22.18.12 + '@inquirer/figures@1.0.15': {} + + '@inquirer/type@3.0.10(@types/node@24.10.1)': + optionalDependencies: + '@types/node': 24.10.1 + '@ioredis/commands@1.4.0': {} '@isaacs/cliui@8.0.2': @@ -12099,6 +13256,24 @@ snapshots: '@jspm/core@2.1.0': {} + '@lmdb/lmdb-darwin-arm64@3.2.6': + optional: true + + '@lmdb/lmdb-darwin-x64@3.2.6': + optional: true + + '@lmdb/lmdb-linux-arm64@3.2.6': + optional: true + + '@lmdb/lmdb-linux-arm@3.2.6': + optional: true + + '@lmdb/lmdb-linux-x64@3.2.6': + optional: true + + '@lmdb/lmdb-win32-x64@3.2.6': + optional: true + '@mapbox/node-pre-gyp@1.0.11': dependencies: detect-libc: 2.1.2 @@ -12155,10 +13330,100 @@ snapshots: unist-util-position-from-estree: 2.0.0 unist-util-stringify-position: 4.0.0 unist-util-visit: 5.0.0 - vfile: 6.0.2 + vfile: 6.0.3 transitivePeerDependencies: - supports-color + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@napi-rs/nice-android-arm-eabi@1.1.1': + optional: true + + '@napi-rs/nice-android-arm64@1.1.1': + optional: true + + '@napi-rs/nice-darwin-arm64@1.1.1': + optional: true + + '@napi-rs/nice-darwin-x64@1.1.1': + optional: true + + '@napi-rs/nice-freebsd-x64@1.1.1': + optional: true + + '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': + optional: true + + '@napi-rs/nice-linux-arm64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-arm64-musl@1.1.1': + optional: true + + '@napi-rs/nice-linux-ppc64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-riscv64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-s390x-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-x64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-x64-musl@1.1.1': + optional: true + + '@napi-rs/nice-openharmony-arm64@1.1.1': + optional: true + + '@napi-rs/nice-win32-arm64-msvc@1.1.1': + optional: true + + '@napi-rs/nice-win32-ia32-msvc@1.1.1': + optional: true + + '@napi-rs/nice-win32-x64-msvc@1.1.1': + optional: true + + '@napi-rs/nice@1.1.1': + optionalDependencies: + '@napi-rs/nice-android-arm-eabi': 1.1.1 + '@napi-rs/nice-android-arm64': 1.1.1 + '@napi-rs/nice-darwin-arm64': 1.1.1 + '@napi-rs/nice-darwin-x64': 1.1.1 + '@napi-rs/nice-freebsd-x64': 1.1.1 + '@napi-rs/nice-linux-arm-gnueabihf': 1.1.1 + '@napi-rs/nice-linux-arm64-gnu': 1.1.1 + '@napi-rs/nice-linux-arm64-musl': 1.1.1 + '@napi-rs/nice-linux-ppc64-gnu': 1.1.1 + '@napi-rs/nice-linux-riscv64-gnu': 1.1.1 + '@napi-rs/nice-linux-s390x-gnu': 1.1.1 + '@napi-rs/nice-linux-x64-gnu': 1.1.1 + '@napi-rs/nice-linux-x64-musl': 1.1.1 + '@napi-rs/nice-openharmony-arm64': 1.1.1 + '@napi-rs/nice-win32-arm64-msvc': 1.1.1 + '@napi-rs/nice-win32-ia32-msvc': 1.1.1 + '@napi-rs/nice-win32-x64-msvc': 1.1.1 + optional: true + '@napi-rs/wasm-runtime@0.2.12': dependencies: '@emnapi/core': 1.6.0 @@ -12368,13 +13633,13 @@ snapshots: '@poppinss/exception@1.2.2': {} - '@preact/preset-vite@2.10.5(@babel/core@7.28.5)(preact@10.29.2)(rollup@4.52.5)(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': + '@preact/preset-vite@2.10.5(@babel/core@7.28.5)(preact@10.29.2)(rollup@4.59.0)(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) '@prefresh/vite': 2.4.11(preact@10.29.2)(vite@6.3.5(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5) debug: 4.4.3 magic-string: 0.30.21 @@ -12389,7 +13654,7 @@ snapshots: '@preact/signals-core@1.14.2': {} - '@preact/signals@2.9.0(preact@10.29.2)': + '@preact/signals@2.9.1(preact@10.29.2)': dependencies: '@preact/signals-core': 1.14.2 preact: 10.29.2 @@ -12464,14 +13729,14 @@ snapshots: valibot: 1.4.1(typescript@5.8.3) vfile: 6.0.2 vite: 6.3.5(@types/node@24.0.13)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - vite-imagetools: 7.1.1(rollup@4.52.5) + vite-imagetools: 7.1.1 zod: 3.22.4 transitivePeerDependencies: - rollup - supports-color - typescript - '@qwik.dev/router@2.0.0-beta.9(patch_hash=e002def78f4681060d18bc24d686c24ee1d1b997c399a2cb1c03baf3b860e4c8)(rollup@4.52.5)(typescript@5.8.3)': + '@qwik.dev/router@2.0.0-beta.9(patch_hash=e002def78f4681060d18bc24d686c24ee1d1b997c399a2cb1c03baf3b860e4c8)(rollup@4.59.0)(typescript@5.8.3)': dependencies: '@mdx-js/mdx': 3.1.1 '@types/mdx': 2.0.13 @@ -12480,7 +13745,7 @@ snapshots: undici: 7.16.0 valibot: 1.4.1(typescript@5.8.3) vfile: 6.0.3 - vite-imagetools: 7.1.1(rollup@4.52.5) + vite-imagetools: 7.1.1(rollup@4.59.0) zod: 3.25.48 transitivePeerDependencies: - rollup @@ -12609,10 +13874,10 @@ snapshots: '@rollup/pluginutils': 5.3.0(rollup@4.52.5) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.5.0(picomatch@4.0.3) + fdir: 6.5.0(picomatch@4.0.4) is-reference: 1.2.1 magic-string: 0.30.21 - picomatch: 4.0.3 + picomatch: 4.0.4 optionalDependencies: rollup: 4.52.5 @@ -12660,80 +13925,169 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.1 + '@rollup/pluginutils@5.3.0': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.4 + '@rollup/pluginutils@5.3.0(rollup@4.52.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.3 + picomatch: 4.0.4 optionalDependencies: rollup: 4.52.5 + '@rollup/pluginutils@5.3.0(rollup@4.59.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.59.0 + '@rollup/rollup-android-arm-eabi@4.52.5': optional: true + '@rollup/rollup-android-arm-eabi@4.59.0': + optional: true + '@rollup/rollup-android-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-arm64@4.52.5': + '@rollup/rollup-android-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.5': + optional: true + + '@rollup/rollup-darwin-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-x64@4.52.5': + optional: true + + '@rollup/rollup-darwin-x64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-arm64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-x64@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true - '@rollup/rollup-darwin-x64@4.52.5': + '@rollup/rollup-linux-arm64-gnu@4.59.0': optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': + '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true - '@rollup/rollup-freebsd-x64@4.52.5': + '@rollup/rollup-linux-arm64-musl@4.59.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': + '@rollup/rollup-linux-loong64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': + '@rollup/rollup-linux-loong64-musl@4.59.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': + '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.5': + '@rollup/rollup-linux-ppc64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.5': + '@rollup/rollup-linux-ppc64-musl@4.59.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true + '@rollup/rollup-linux-riscv64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true + '@rollup/rollup-linux-s390x-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true + '@rollup/rollup-linux-x64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.52.5': optional: true + '@rollup/rollup-linux-x64-musl@4.59.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.59.0': + optional: true + '@rollup/rollup-openharmony-arm64@4.52.5': optional: true + '@rollup/rollup-openharmony-arm64@4.59.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true + '@rollup/rollup-win32-arm64-msvc@4.59.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true + '@rollup/rollup-win32-ia32-msvc@4.59.0': + optional: true + '@rollup/rollup-win32-x64-gnu@4.52.5': optional: true + '@rollup/rollup-win32-x64-gnu@4.59.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true + '@rollup/rollup-win32-x64-msvc@4.59.0': + optional: true + '@rtsao/scc@1.1.0': {} '@shikijs/core@1.29.2': @@ -13096,6 +14450,13 @@ snapshots: tailwindcss: 4.1.16 vite: 7.1.12(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + '@tailwindcss/vite@4.1.17(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': + dependencies: + '@tailwindcss/node': 4.1.17 + '@tailwindcss/oxide': 4.1.17 + tailwindcss: 4.1.17 + vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + '@tailwindcss/vite@4.1.17(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@tailwindcss/node': 4.1.17 @@ -13231,6 +14592,13 @@ snapshots: mkdirp: 1.0.4 path-browserify: 1.0.1 + '@ts-morph/common@0.22.0': + dependencies: + fast-glob: 3.3.3 + minimatch: 9.0.5 + mkdirp: 3.0.1 + path-browserify: 1.0.1 + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -13551,8 +14919,8 @@ snapshots: '@typescript-eslint/project-service@8.36.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.8.3) - '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/tsconfig-utils': 8.36.0(typescript@5.8.3) + '@typescript-eslint/types': 8.36.0 debug: 4.4.3 typescript: 5.8.3 transitivePeerDependencies: @@ -13989,7 +15357,7 @@ snapshots: glob: 10.4.5 graceful-fs: 4.2.11 node-gyp-build: 4.8.4 - picomatch: 4.0.3 + picomatch: 4.0.4 resolve-from: 5.0.0 transitivePeerDependencies: - encoding @@ -14205,6 +15573,10 @@ snapshots: recast: 0.23.11 vinxi: 0.5.8(@types/node@24.10.1)(db0@0.3.4)(ioredis@5.8.2)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(rolldown@1.0.0-beta.52)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + '@vitejs/plugin-basic-ssl@1.2.0(vite@6.4.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': + dependencies: + vite: 6.4.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + '@vitejs/plugin-react@5.1.1(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.5 @@ -14574,6 +15946,10 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -14581,6 +15957,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ajv@8.6.3: dependencies: fast-deep-equal: 3.1.3 @@ -14598,6 +15981,10 @@ snapshots: dependencies: type-fest: 0.21.3 + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -14864,6 +16251,17 @@ snapshots: baseline-browser-mapping@2.8.20: {} + beasties@0.3.2: + dependencies: + css-select: 5.2.2 + css-what: 6.2.2 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + htmlparser2: 10.1.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-media-query-parser: 0.2.3 + before-after-hook@4.0.0: {} big.js@5.2.2: {} @@ -15110,10 +16508,21 @@ snapshots: dependencies: restore-cursor: 3.1.0 + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + cli-spinners@2.9.2: {} + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + cli-width@3.0.0: {} + cli-width@4.1.0: {} + clipboardy@4.0.0: dependencies: execa: 8.0.1 @@ -15138,6 +16547,8 @@ snapshots: code-block-writer@10.1.1: {} + code-block-writer@12.0.0: {} + collapse-white-space@2.1.0: {} color-convert@2.0.1: @@ -15211,6 +16622,8 @@ snapshots: convert-hrtime@3.0.0: {} + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} cookie-es@1.2.2: {} @@ -15593,6 +17006,10 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + + environment@1.1.0: {} + errno@0.1.8: dependencies: prr: 1.0.1 @@ -15835,12 +17252,12 @@ snapshots: esbuild: 0.17.6 import-meta-resolve: 2.2.2 - esbuild-plugin-solid@0.5.0(esbuild@0.25.11)(solid-js@1.9.9): + esbuild-plugin-solid@0.5.0(esbuild@0.28.0)(solid-js@1.9.9): dependencies: '@babel/core': 7.28.5 '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) babel-preset-solid: 1.9.9(@babel/core@7.28.5)(solid-js@1.9.9) - esbuild: 0.25.11 + esbuild: 0.28.0 solid-js: 1.9.9 transitivePeerDependencies: - supports-color @@ -16022,6 +17439,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.11 '@esbuild/win32-x64': 0.25.11 + esbuild@0.28.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.0 + '@esbuild/android-arm': 0.28.0 + '@esbuild/android-arm64': 0.28.0 + '@esbuild/android-x64': 0.28.0 + '@esbuild/darwin-arm64': 0.28.0 + '@esbuild/darwin-x64': 0.28.0 + '@esbuild/freebsd-arm64': 0.28.0 + '@esbuild/freebsd-x64': 0.28.0 + '@esbuild/linux-arm': 0.28.0 + '@esbuild/linux-arm64': 0.28.0 + '@esbuild/linux-ia32': 0.28.0 + '@esbuild/linux-loong64': 0.28.0 + '@esbuild/linux-mips64el': 0.28.0 + '@esbuild/linux-ppc64': 0.28.0 + '@esbuild/linux-riscv64': 0.28.0 + '@esbuild/linux-s390x': 0.28.0 + '@esbuild/linux-x64': 0.28.0 + '@esbuild/netbsd-arm64': 0.28.0 + '@esbuild/netbsd-x64': 0.28.0 + '@esbuild/openbsd-arm64': 0.28.0 + '@esbuild/openbsd-x64': 0.28.0 + '@esbuild/openharmony-arm64': 0.28.0 + '@esbuild/sunos-x64': 0.28.0 + '@esbuild/win32-arm64': 0.28.0 + '@esbuild/win32-ia32': 0.28.0 + '@esbuild/win32-x64': 0.28.0 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -16542,6 +17988,8 @@ snapshots: eventemitter3@4.0.7: {} + eventemitter3@5.0.4: {} + events-universal@1.0.1: dependencies: bare-events: 2.8.1 @@ -16648,6 +18096,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.2: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -16660,6 +18110,10 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + fflate@0.7.4: {} figures@3.2.0: @@ -16886,6 +18340,8 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + glob@10.4.5: dependencies: foreground-child: 3.3.1 @@ -17148,6 +18604,13 @@ snapshots: html-void-elements@3.0.0: {} + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + http-cache-semantics@4.2.0: {} http-errors@2.0.0: @@ -17393,6 +18856,12 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.4.0 + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -17525,6 +18994,16 @@ snapshots: istanbul-lib-coverage@3.2.2: {} + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 @@ -17648,6 +19127,8 @@ snapshots: json5@2.2.3: {} + jsonc-parser@3.3.1: {} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -17794,6 +19275,31 @@ snapshots: untun: 0.1.3 uqr: 0.1.2 + listr2@8.2.5: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + lmdb@3.2.6: + dependencies: + msgpackr: 1.11.12 + node-addon-api: 6.1.0 + node-gyp-build-optional-packages: 5.2.2 + ordered-binary: 1.6.1 + weak-lru-cache: 1.2.2 + optionalDependencies: + '@lmdb/lmdb-darwin-arm64': 3.2.6 + '@lmdb/lmdb-darwin-x64': 3.2.6 + '@lmdb/lmdb-linux-arm': 3.2.6 + '@lmdb/lmdb-linux-arm64': 3.2.6 + '@lmdb/lmdb-linux-x64': 3.2.6 + '@lmdb/lmdb-win32-x64': 3.2.6 + optional: true + load-tsconfig@0.2.5: {} loader-utils@2.0.4: @@ -17837,6 +19343,14 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -17861,6 +19375,10 @@ snapshots: lz-string@1.5.0: {} + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -18065,7 +19583,7 @@ snapshots: trim-lines: 3.0.1 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 - vfile: 6.0.2 + vfile: 6.0.3 mdast-util-to-markdown@1.5.0: dependencies: @@ -18574,6 +20092,8 @@ snapshots: mimic-fn@4.0.0: {} + mimic-function@5.0.1: {} + mimic-response@1.0.1: {} mimic-response@3.1.0: {} @@ -18629,6 +20149,8 @@ snapshots: mkdirp@1.0.4: {} + mkdirp@3.0.1: {} + mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -18646,10 +20168,29 @@ snapshots: ms@2.1.3: {} + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.12: + optionalDependencies: + msgpackr-extract: 3.0.3 + optional: true + muggle-string@0.4.1: {} mute-stream@0.0.8: {} + mute-stream@2.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -18777,6 +20318,9 @@ snapshots: node-addon-api@1.7.2: optional: true + node-addon-api@6.1.0: + optional: true + node-addon-api@7.1.1: {} node-fetch-native@1.6.7: {} @@ -18791,6 +20335,11 @@ snapshots: node-forge@1.3.1: {} + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.1.2 + optional: true + node-gyp-build@4.8.4: {} node-html-parser@6.1.13: @@ -18933,6 +20482,10 @@ snapshots: dependencies: mimic-fn: 4.0.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + oniguruma-parser@0.12.1: {} oniguruma-to-es@2.3.0: @@ -18990,6 +20543,9 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + ordered-binary@1.6.1: + optional: true + outdent@0.8.0: {} own-keys@1.0.1: @@ -19067,6 +20623,16 @@ snapshots: parse-statements@1.0.11: {} + parse5-html-rewriting-stream@7.0.0: + dependencies: + entities: 4.5.0 + parse5: 7.3.0 + parse5-sax-parser: 7.0.0 + + parse5-sax-parser@7.0.0: + dependencies: + parse5: 7.3.0 + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -19130,6 +20696,8 @@ snapshots: picomatch@4.0.3: {} + picomatch@4.0.4: {} + pidtree@0.6.0: {} pify@4.0.1: @@ -19137,6 +20705,10 @@ snapshots: pirates@4.0.7: {} + piscina@4.8.0: + optionalDependencies: + '@napi-rs/nice': 1.1.1 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -19188,6 +20760,8 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 + postcss-media-query-parser@0.2.3: {} + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -19484,7 +21058,7 @@ snapshots: dependencies: '@types/estree': 1.0.8 estree-util-build-jsx: 3.0.1 - vfile: 6.0.2 + vfile: 6.0.3 recma-jsx@1.0.1(acorn@8.15.0): dependencies: @@ -19500,14 +21074,14 @@ snapshots: '@types/estree': 1.0.8 esast-util-from-js: 2.0.1 unified: 11.0.5 - vfile: 6.0.2 + vfile: 6.0.3 recma-stringify@1.0.0: dependencies: '@types/estree': 1.0.8 estree-util-to-js: 2.0.0 unified: 11.0.5 - vfile: 6.0.2 + vfile: 6.0.3 redent@3.0.0: dependencies: @@ -19520,6 +21094,8 @@ snapshots: dependencies: redis-errors: 1.2.0 + reflect-metadata@0.2.2: {} + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -19645,7 +21221,7 @@ snapshots: '@types/mdast': 4.0.4 mdast-util-to-hast: 13.2.0 unified: 11.0.5 - vfile: 6.0.2 + vfile: 6.0.3 remark-rehype@9.1.0: dependencies: @@ -19693,6 +21269,11 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} rfdc@1.4.1: {} @@ -19798,7 +21379,7 @@ snapshots: rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-beta.52)(rollup@4.52.5): dependencies: open: 8.4.2 - picomatch: 4.0.3 + picomatch: 4.0.4 source-map: 0.7.6 yargs: 17.7.2 optionalDependencies: @@ -19851,6 +21432,37 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 + rollup@4.59.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 + fsevents: 2.3.3 + rrweb-cssom@0.8.0: {} run-applescript@7.1.0: {} @@ -19861,6 +21473,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -19898,6 +21514,14 @@ snapshots: safer-buffer@2.1.2: {} + sass@1.85.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.4 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + sass@1.93.2: dependencies: chokidar: 4.0.3 @@ -19954,6 +21578,8 @@ snapshots: dependencies: lru-cache: 6.0.0 + semver@7.7.1: {} + semver@7.7.3: {} send@0.19.0: @@ -20187,6 +21813,16 @@ snapshots: slash@5.1.0: {} + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + smart-buffer@4.2.0: {} smob@1.5.0: {} @@ -20243,6 +21879,8 @@ snapshots: source-map@0.6.1: {} + source-map@0.7.4: {} + source-map@0.7.6: {} source-map@0.8.0-beta.0: @@ -20466,6 +22104,18 @@ snapshots: transitivePeerDependencies: - picomatch + svelte-check@4.3.3(picomatch@4.0.4)(svelte@5.42.1)(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.42.1 + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + svelte-eslint-parser@1.4.0(svelte@5.42.1): dependencies: eslint-scope: 8.4.0 @@ -20681,6 +22331,11 @@ snapshots: '@ts-morph/common': 0.11.1 code-block-writer: 10.1.1 + ts-morph@21.0.1: + dependencies: + '@ts-morph/common': 0.22.0 + code-block-writer: 12.0.0 + ts-node@10.9.1(@types/node@14.18.33)(typescript@4.9.5): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -20859,9 +22514,9 @@ snapshots: dependencies: esbuild: 0.15.18 - tsup-preset-solid@2.2.0(esbuild@0.25.11)(solid-js@1.9.9)(tsup@8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1)): + tsup-preset-solid@2.2.0(esbuild@0.28.0)(solid-js@1.9.9)(tsup@8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1)): dependencies: - esbuild-plugin-solid: 0.5.0(esbuild@0.25.11)(solid-js@1.9.9) + esbuild-plugin-solid: 0.5.0(esbuild@0.28.0)(solid-js@1.9.9) tsup: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1) transitivePeerDependencies: - esbuild @@ -21138,7 +22793,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.0 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 pkg-types: 2.3.0 scule: 1.3.0 strip-literal: 3.1.0 @@ -21230,7 +22885,7 @@ snapshots: unplugin-utils@0.3.1: dependencies: pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 unplugin-vue@7.0.3(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(vue@3.5.22(typescript@5.8.3))(yaml@2.8.1): dependencies: @@ -21329,6 +22984,10 @@ snapshots: v8-compile-cache-lib@3.0.1: {} + valibot@1.2.0(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 + valibot@1.4.1(typescript@5.8.3): optionalDependencies: typescript: 5.8.3 @@ -21492,9 +23151,17 @@ snapshots: dependencies: vite: 7.1.12(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - vite-imagetools@7.1.1(rollup@4.52.5): + vite-imagetools@7.1.1: dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0 + imagetools-core: 7.1.0 + sharp: 0.34.4 + transitivePeerDependencies: + - rollup + + vite-imagetools@7.1.1(rollup@4.59.0): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) imagetools-core: 7.1.0 sharp: 0.34.4 transitivePeerDependencies: @@ -21774,6 +23441,26 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 + vite@6.4.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.6 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + jiti: 2.6.1 + less: 4.4.2 + lightningcss: 1.30.2 + sass: 1.85.0 + stylus: 0.62.0 + terser: 5.44.0 + tsx: 4.20.6 + yaml: 2.8.1 + vite@7.0.4(@types/node@24.0.13)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11 @@ -21875,6 +23562,26 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 + vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + jiti: 2.6.1 + less: 4.4.2 + lightningcss: 1.30.2 + sass: 1.85.0 + stylus: 0.62.0 + terser: 5.44.0 + tsx: 4.20.6 + yaml: 2.8.1 + vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11 @@ -22157,10 +23864,18 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + watchpack@2.4.2: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + wcwidth@1.0.1: dependencies: defaults: 1.0.4 + weak-lru-cache@1.2.2: + optional: true + web-vitals@0.2.4: {} webidl-conversions@3.0.1: {} @@ -22363,6 +24078,8 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors-cjs@2.1.3: {} + yoga-wasm-web@0.3.3: {} youch-core@0.3.3: From 418dc5109784de2d25f588df6ef09b84bbaa6d9d Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sun, 24 May 2026 23:05:44 -0400 Subject: [PATCH 15/18] Ignore docs/superpowers directory --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index acd6a09b..35fa9a41 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ backups .netlify .vercel +# Claude generated files +docs/superpowers + # Others logs *.log From 3cd1d893f25b419342ac35ecf0a8f0410bc6208a Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Fri, 29 May 2026 15:59:04 -0400 Subject: [PATCH 16/18] Expand Angular playground and fix AOT compilation issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix FormischFieldArray to use signal inputs and contentChild() for AOT compatibility - Replace @formkit/auto-animate/angular (no Ivy metadata) with a custom AutoAnimateDirective using afterNextRender - Fix (change) → (input) on text-input and slider so validate: 'input' fires on every keystroke - Replace effect + setTimeout pattern in ExpandableComponent with afterRenderEffect - Add nested and special playground routes matching React 1:1 - Add all shared UI components (checkbox, select, radio, slider, file-input, expandable, etc.) --- .../FormischField/FormischField.test.ts | 4 +- .../FormischFieldArray/FormischFieldArray.ts | 27 +-- .../FormischForm/FormischForm.test.ts | 4 +- .../injectField/injectField.test-d.ts | 56 +++-- .../injectFieldArray.test-d.ts | 37 ++- .../functions/injectForm/injectForm.test-d.ts | 41 ++-- playgrounds/angular/package.json | 3 +- playgrounds/angular/src/app.component.ts | 91 ++++++- playgrounds/angular/src/app.routes.ts | 14 ++ .../src/components/action-button.component.ts | 40 ++++ .../src/components/auto-animate.directive.ts | 20 ++ .../src/components/button-group.component.ts | 21 ++ .../src/components/checkbox.component.ts | 54 +++++ .../src/components/color-button.component.ts | 40 ++++ .../src/components/expandable.component.ts | 56 +++++ .../src/components/file-input.component.ts | 77 ++++++ .../src/components/form-error.component.ts | 31 +++ .../src/components/form-footer.component.ts | 23 +- .../src/components/form-header.component.ts | 23 +- .../src/components/input-errors.component.ts | 25 ++ .../src/components/input-label.component.ts | 50 ++++ .../src/components/radio-group.component.ts | 59 +++++ .../angular/src/components/radio.component.ts | 34 +++ .../src/components/select.component.ts | 95 ++++++++ .../src/components/slider.component.ts | 54 +++++ .../src/components/spinner.component.ts | 16 ++ .../src/components/text-input.component.ts | 36 ++- .../components/unstyled-button.component.ts | 60 +++++ .../src/icons/angle-down-icon.component.ts | 24 ++ .../src/routes/nested/nested.component.ts | 220 +++++++++++++++++ .../src/routes/special/special.component.ts | 226 ++++++++++++++++++ .../src/routes/todos/todos.component.ts | 159 +++++++----- .../angular/src/utils/disable-transitions.ts | 8 + pnpm-lock.yaml | 3 + 34 files changed, 1545 insertions(+), 186 deletions(-) create mode 100644 playgrounds/angular/src/components/action-button.component.ts create mode 100644 playgrounds/angular/src/components/auto-animate.directive.ts create mode 100644 playgrounds/angular/src/components/button-group.component.ts create mode 100644 playgrounds/angular/src/components/checkbox.component.ts create mode 100644 playgrounds/angular/src/components/color-button.component.ts create mode 100644 playgrounds/angular/src/components/expandable.component.ts create mode 100644 playgrounds/angular/src/components/file-input.component.ts create mode 100644 playgrounds/angular/src/components/form-error.component.ts create mode 100644 playgrounds/angular/src/components/input-errors.component.ts create mode 100644 playgrounds/angular/src/components/input-label.component.ts create mode 100644 playgrounds/angular/src/components/radio-group.component.ts create mode 100644 playgrounds/angular/src/components/radio.component.ts create mode 100644 playgrounds/angular/src/components/select.component.ts create mode 100644 playgrounds/angular/src/components/slider.component.ts create mode 100644 playgrounds/angular/src/components/spinner.component.ts create mode 100644 playgrounds/angular/src/components/unstyled-button.component.ts create mode 100644 playgrounds/angular/src/icons/angle-down-icon.component.ts create mode 100644 playgrounds/angular/src/routes/nested/nested.component.ts create mode 100644 playgrounds/angular/src/routes/special/special.component.ts create mode 100644 playgrounds/angular/src/utils/disable-transitions.ts diff --git a/frameworks/angular/src/components/FormischField/FormischField.test.ts b/frameworks/angular/src/components/FormischField/FormischField.test.ts index 80fa18c7..9e08dd6d 100644 --- a/frameworks/angular/src/components/FormischField/FormischField.test.ts +++ b/frameworks/angular/src/components/FormischField/FormischField.test.ts @@ -34,14 +34,14 @@ describe('FormischField', () => { it('renders the template with the field store', async () => { const fixture = TestBed.createComponent(TestHost); await fixture.whenStable(); - const input = fixture.nativeElement.querySelector('[data-testid="input"]'); + const input = (fixture.nativeElement as HTMLElement).querySelector('[data-testid="input"]'); expect(input).not.toBeNull(); }); it('passes the field name prop to the template context', async () => { const fixture = TestBed.createComponent(TestHost); await fixture.whenStable(); - const input = fixture.nativeElement.querySelector('[data-testid="input"]'); + const input = (fixture.nativeElement as HTMLElement).querySelector('[data-testid="input"]'); expect(input.getAttribute('name')).not.toBeNull(); }); }); diff --git a/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts index b44dfdf0..9ec87ba8 100644 --- a/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts +++ b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts @@ -1,13 +1,13 @@ import { Component, computed, - ContentChild, - Input, + contentChild, input, type InputSignal, + type Signal, TemplateRef, } from '@angular/core'; -import { NgIf, NgTemplateOutlet } from '@angular/common'; +import { NgTemplateOutlet } from '@angular/common'; import { getFieldBool, getFieldStore, @@ -38,25 +38,24 @@ import type { FieldArrayStore, FormStore } from '../../types/index.ts'; @Component({ selector: 'formisch-field-array', standalone: true, - imports: [NgIf, NgTemplateOutlet], + imports: [NgTemplateOutlet], template: ` - + @if (template()) { + + } `, }) export class FormischFieldArray< TSchema extends Schema = Schema, TFieldArrayPath extends RequiredPath = RequiredPath, > { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input({ isSignal: true } as any) of: InputSignal> = input.required>(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input({ isSignal: true } as any) path: InputSignal, TFieldArrayPath>> = input.required, TFieldArrayPath>>(); + readonly of: InputSignal> = input.required>(); + readonly path: InputSignal, TFieldArrayPath>> = input.required, TFieldArrayPath>>(); - @ContentChild(TemplateRef) protected template: TemplateRef<{ $implicit: FieldArrayStore }> | undefined; + protected readonly template: Signal | undefined> = contentChild(TemplateRef); private readonly internalFieldStore = computed( () => diff --git a/frameworks/angular/src/components/FormischForm/FormischForm.test.ts b/frameworks/angular/src/components/FormischForm/FormischForm.test.ts index d61d300c..06330439 100644 --- a/frameworks/angular/src/components/FormischForm/FormischForm.test.ts +++ b/frameworks/angular/src/components/FormischForm/FormischForm.test.ts @@ -32,14 +32,14 @@ describe('FormischForm', () => { it('renders a native form element', async () => { const fixture = TestBed.createComponent(TestHost); await fixture.whenStable(); - const form = fixture.nativeElement.querySelector('form'); + const form = (fixture.nativeElement as HTMLElement).querySelector('form'); expect(form).not.toBeNull(); }); it('sets novalidate on the form element', async () => { const fixture = TestBed.createComponent(TestHost); await fixture.whenStable(); - const form = fixture.nativeElement.querySelector('form'); + const form = (fixture.nativeElement as HTMLElement).querySelector('form'); expect(form.hasAttribute('novalidate')).toBe(true); }); diff --git a/frameworks/angular/src/functions/injectField/injectField.test-d.ts b/frameworks/angular/src/functions/injectField/injectField.test-d.ts index 856212a1..eb99c358 100644 --- a/frameworks/angular/src/functions/injectField/injectField.test-d.ts +++ b/frameworks/angular/src/functions/injectField/injectField.test-d.ts @@ -5,52 +5,55 @@ import type { FieldStore } from '../../types/index.ts'; import { injectForm } from '../injectForm/index.ts'; import { injectField } from './injectField.ts'; -const schemaSimple = v.object({ name: v.string() }); -const schemaMixed = v.object({ name: v.string(), age: v.number() }); -const schemaEmail = v.object({ email: v.pipe(v.string(), v.email()) }); -const schemaNested = v.object({ - user: v.object({ email: v.string() }), - tags: v.array(v.string()), -}); - -declare const formSimple: ReturnType>; -declare const formMixed: ReturnType>; -declare const formEmail: ReturnType>; -declare const formNested: ReturnType>; - describe('injectField', () => { test('should return a FieldStore typed against the form schema and path', () => { - const field = injectField(formSimple, { path: ['name'] }); + const schema = v.object({ name: v.string() }); + const form = injectForm({ schema }); + const field = injectField(form, { path: ['name'] }); - expectTypeOf(field).toEqualTypeOf>(); + expectTypeOf(field).toEqualTypeOf>(); }); test('should narrow input type for primitive leaves', () => { - expectTypeOf(injectField(formMixed, { path: ['name'] }).input).toEqualTypeOf< + const schema = v.object({ name: v.string(), age: v.number() }); + const form = injectForm({ schema }); + + expectTypeOf(injectField(form, { path: ['name'] }).input).toEqualTypeOf< Signal >(); - expectTypeOf(injectField(formMixed, { path: ['age'] }).input).toEqualTypeOf< + expectTypeOf(injectField(form, { path: ['age'] }).input).toEqualTypeOf< Signal >(); }); test('should narrow onInput value type for primitive leaves', () => { + const schema = v.object({ name: v.string() }); + const form = injectForm({ schema }); + expectTypeOf( - injectField(formSimple, { path: ['name'] }).onInput + injectField(form, { path: ['name'] }).onInput ).toEqualTypeOf<(value: string | undefined) => void>(); }); test('should narrow input type through nested object and array index paths', () => { + const schema = v.object({ + user: v.object({ email: v.string() }), + tags: v.array(v.string()), + }); + const form = injectForm({ schema }); + expectTypeOf( - injectField(formNested, { path: ['user', 'email'] }).input + injectField(form, { path: ['user', 'email'] }).input ).toEqualTypeOf>(); expectTypeOf( - injectField(formNested, { path: ['tags', 0] }).input + injectField(form, { path: ['tags', 0] }).input ).toEqualTypeOf>(); }); test('should expose Signal boolean state properties', () => { - const field = injectField(formEmail, { path: ['email'] }); + const schema = v.object({ email: v.pipe(v.string(), v.email()) }); + const form = injectForm({ schema }); + const field = injectField(form, { path: ['email'] }); expectTypeOf(field.errors).toEqualTypeOf>(); expectTypeOf(field.isTouched).toEqualTypeOf>(); @@ -59,17 +62,22 @@ describe('injectField', () => { }); test('should expose props with name and autofocus', () => { - const field = injectField(formEmail, { path: ['email'] }); + const schema = v.object({ email: v.pipe(v.string(), v.email()) }); + const form = injectForm({ schema }); + const field = injectField(form, { path: ['email'] }); expectTypeOf(field.props.name).toEqualTypeOf(); expectTypeOf(field.props.autofocus).toEqualTypeOf(); }); test('should reject invalid paths', () => { + const schema = v.object({ name: v.string() }); + const form = injectForm({ schema }); + // @ts-expect-error nonexistent field - injectField(formSimple, { path: ['nonexistent'] }); + injectField(form, { path: ['nonexistent'] }); // @ts-expect-error path through a string leaf - injectField(formSimple, { path: ['name', 'nested'] }); + injectField(form, { path: ['name', 'nested'] }); }); }); diff --git a/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts index bfc2857c..e40f1f78 100644 --- a/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts +++ b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.test-d.ts @@ -1,25 +1,24 @@ import type { Signal } from '@angular/core'; import * as v from 'valibot'; -import { expectTypeOf } from 'vitest'; +import { describe, expectTypeOf, test } from 'vitest'; import type { FieldArrayStore } from '../../types/index.ts'; +import { injectForm } from '../injectForm/index.ts'; import { injectFieldArray } from './injectFieldArray.ts'; -const Schema = v.object({ - todos: v.array(v.object({ title: v.string() })), -}); - -declare const fieldArray: ReturnType< - typeof injectFieldArray ->; +describe('injectFieldArray', () => { + test('should return a FieldArrayStore typed against the form schema and path', () => { + const schema = v.object({ + todos: v.array(v.object({ title: v.string() })), + }); + const form = injectForm({ schema }); + const fieldArray = injectFieldArray(form, { path: ['todos'] }); -expectTypeOf(fieldArray).toMatchTypeOf< - FieldArrayStore ->(); -expectTypeOf(fieldArray.path).toEqualTypeOf<['todos']>(); -expectTypeOf(fieldArray.items).toEqualTypeOf>(); -expectTypeOf(fieldArray.errors).toEqualTypeOf< - Signal<[string, ...string[]] | null> ->(); -expectTypeOf(fieldArray.isTouched).toEqualTypeOf>(); -expectTypeOf(fieldArray.isDirty).toEqualTypeOf>(); -expectTypeOf(fieldArray.isValid).toEqualTypeOf>(); + expectTypeOf(fieldArray).toExtend>(); + expectTypeOf(fieldArray.path).toEqualTypeOf<['todos']>(); + expectTypeOf(fieldArray.items).toEqualTypeOf>(); + expectTypeOf(fieldArray.errors).toEqualTypeOf>(); + expectTypeOf(fieldArray.isTouched).toEqualTypeOf>(); + expectTypeOf(fieldArray.isDirty).toEqualTypeOf>(); + expectTypeOf(fieldArray.isValid).toEqualTypeOf>(); + }); +}); diff --git a/frameworks/angular/src/functions/injectForm/injectForm.test-d.ts b/frameworks/angular/src/functions/injectForm/injectForm.test-d.ts index f34b55e2..da3edbf7 100644 --- a/frameworks/angular/src/functions/injectForm/injectForm.test-d.ts +++ b/frameworks/angular/src/functions/injectForm/injectForm.test-d.ts @@ -1,21 +1,34 @@ import type { Signal } from '@angular/core'; import * as v from 'valibot'; -import { expectTypeOf } from 'vitest'; +import { describe, expectTypeOf, test } from 'vitest'; import type { FormStore } from '../../types/index.ts'; import { injectForm } from './injectForm.ts'; -const Schema = v.object({ - email: v.pipe(v.string(), v.email()), - password: v.pipe(v.string(), v.minLength(8)), -}); +describe('injectForm', () => { + test('should return a FormStore typed against the schema', () => { + const schema = v.object({ + email: v.pipe(v.string(), v.email()), + password: v.pipe(v.string(), v.minLength(8)), + }); + const form = injectForm({ schema }); + + expectTypeOf(form).toExtend>(); + expectTypeOf(form.isSubmitting).toEqualTypeOf>(); + expectTypeOf(form.isSubmitted).toEqualTypeOf>(); + expectTypeOf(form.isValidating).toEqualTypeOf>(); + expectTypeOf(form.isTouched).toEqualTypeOf>(); + expectTypeOf(form.isDirty).toEqualTypeOf>(); + expectTypeOf(form.isValid).toEqualTypeOf>(); + expectTypeOf(form.errors).toEqualTypeOf>(); + }); -declare const form: ReturnType>; + test('should accept full, partial, and reject mistyped initialInput', () => { + const schema = v.object({ name: v.string(), age: v.number() }); -expectTypeOf(form).toMatchTypeOf>(); -expectTypeOf(form.isSubmitting).toEqualTypeOf>(); -expectTypeOf(form.isSubmitted).toEqualTypeOf>(); -expectTypeOf(form.isValidating).toEqualTypeOf>(); -expectTypeOf(form.isTouched).toEqualTypeOf>(); -expectTypeOf(form.isDirty).toEqualTypeOf>(); -expectTypeOf(form.isValid).toEqualTypeOf>(); -expectTypeOf(form.errors).toEqualTypeOf>(); + injectForm({ schema, initialInput: { name: 'John', age: 30 } }); + injectForm({ schema, initialInput: { name: 'John' } }); + + // @ts-expect-error wrong leaf type + injectForm({ schema, initialInput: { name: 123 } }); + }); +}); diff --git a/playgrounds/angular/package.json b/playgrounds/angular/package.json index 380b3226..c904e9cf 100644 --- a/playgrounds/angular/package.json +++ b/playgrounds/angular/package.json @@ -17,7 +17,8 @@ "@formisch/angular": "workspace:*", "@tailwindcss/vite": "^4.1.17", "clsx": "^2.1.1", - "valibot": "^1.2.0" + "valibot": "^1.2.0", + "@formkit/auto-animate": "^0.8.4" }, "devDependencies": { "@analogjs/vite-plugin-angular": "^1.10.0", diff --git a/playgrounds/angular/src/app.component.ts b/playgrounds/angular/src/app.component.ts index 1e673d7c..99c83c5a 100644 --- a/playgrounds/angular/src/app.component.ts +++ b/playgrounds/angular/src/app.component.ts @@ -1,6 +1,29 @@ -import { Component } from '@angular/core'; -import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { + Component, + ElementRef, + HostListener, + inject, + signal, + viewChild, +} from '@angular/core'; +import { + NavigationEnd, + Router, + RouterLink, + RouterLinkActive, + RouterOutlet, +} from '@angular/router'; +import { filter } from 'rxjs/operators'; +import { disableTransitions } from './utils/disable-transitions.ts'; +interface IndicatorStyle { + left: string; + width: string; +} + +/** + * Root application component with tab navigation and sliding indicator. + */ @Component({ selector: 'app-root', standalone: true, @@ -8,15 +31,29 @@ import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; template: `
-
@@ -29,5 +66,49 @@ export class AppComponent { { path: '/login', label: 'Login' }, { path: '/payment', label: 'Payment' }, { path: '/todos', label: 'Todos' }, + { path: '/special', label: 'Special' }, + { path: '/nested', label: 'Nested' }, ]; + + private readonly navEl = + viewChild>('navEl'); + protected readonly indicatorStyle = signal( + undefined + ); + + constructor() { + const router = inject(Router); + router.events + .pipe(filter((e) => e instanceof NavigationEnd)) + .subscribe(() => setTimeout(() => this.updateIndicatorStyle())); + } + + @HostListener('window:resize') + onResize(): void { + disableTransitions(); + this.updateIndicatorStyle(); + } + + private updateIndicatorStyle(): void { + const nav = this.navEl()?.nativeElement; + if (!nav) return; + const activeEl = [...nav.children].find( + (el) => (el as HTMLAnchorElement).href?.endsWith(location.pathname) + ) as HTMLAnchorElement | undefined; + this.indicatorStyle.set( + activeEl + ? { + left: `${activeEl.offsetLeft}px`, + width: `${activeEl.offsetWidth}px`, + } + : undefined + ); + } + + protected scrollIntoView(event: MouseEvent): void { + (event.currentTarget as HTMLElement).scrollIntoView({ + block: 'nearest', + inline: 'center', + }); + } } diff --git a/playgrounds/angular/src/app.routes.ts b/playgrounds/angular/src/app.routes.ts index 4b13f41d..2f6223dc 100644 --- a/playgrounds/angular/src/app.routes.ts +++ b/playgrounds/angular/src/app.routes.ts @@ -23,4 +23,18 @@ export const routes: Routes = [ (m) => m.TodosComponent ), }, + { + path: 'special', + loadComponent: () => + import('./routes/special/special.component.ts').then( + (m) => m.SpecialComponent + ), + }, + { + path: 'nested', + loadComponent: () => + import('./routes/nested/nested.component.ts').then( + (m) => m.NestedComponent + ), + }, ]; diff --git a/playgrounds/angular/src/components/action-button.component.ts b/playgrounds/angular/src/components/action-button.component.ts new file mode 100644 index 00000000..479353da --- /dev/null +++ b/playgrounds/angular/src/components/action-button.component.ts @@ -0,0 +1,40 @@ +import { Component, input, output } from '@angular/core'; +import clsx from 'clsx'; +import { UnstyledButtonComponent } from './unstyled-button.component.ts'; + +/** + * Button used for navigation, to confirm form entries, or perform actions. + */ +@Component({ + selector: 'app-action-button', + standalone: true, + imports: [UnstyledButtonComponent], + template: ` + + {{ label() }} + + `, +}) +export class ActionButtonComponent { + readonly type = input<'button' | 'submit' | 'reset'>('button'); + readonly variant = input.required<'primary' | 'secondary'>(); + readonly label = input.required(); + readonly loading = input(false); + readonly onClick = input<(() => unknown) | undefined>(undefined); + readonly clicked = output(); + + protected readonly classes = () => + clsx( + 'relative flex cursor-pointer items-center justify-center rounded-2xl px-5 py-2.5 font-medium no-underline transition-colors md:text-lg lg:rounded-2xl lg:px-6 lg:py-3 lg:text-xl', + this.variant() === 'primary' && + 'bg-sky-600 text-white hover:bg-sky-600/80 dark:bg-sky-400 dark:text-gray-900 dark:hover:bg-sky-400/80', + this.variant() === 'secondary' && + 'bg-sky-600/10 text-sky-600 hover:bg-sky-600/20 dark:bg-sky-400/10 dark:text-sky-400 dark:hover:bg-sky-400/20' + ); +} diff --git a/playgrounds/angular/src/components/auto-animate.directive.ts b/playgrounds/angular/src/components/auto-animate.directive.ts new file mode 100644 index 00000000..ba54d150 --- /dev/null +++ b/playgrounds/angular/src/components/auto-animate.directive.ts @@ -0,0 +1,20 @@ +import { afterNextRender, Directive, ElementRef, inject, input } from '@angular/core'; +import autoAnimate, { type AutoAnimateOptions } from '@formkit/auto-animate'; + +/** + * Directive that enables auto-animate on the host element. + */ +@Directive({ + selector: '[auto-animate]', + standalone: true, +}) +export class AutoAnimateDirective { + readonly options = input>(); + + constructor() { + const el = inject>(ElementRef); + afterNextRender(() => { + autoAnimate(el.nativeElement, this.options() ?? {}); + }); + } +} diff --git a/playgrounds/angular/src/components/button-group.component.ts b/playgrounds/angular/src/components/button-group.component.ts new file mode 100644 index 00000000..56a27917 --- /dev/null +++ b/playgrounds/angular/src/components/button-group.component.ts @@ -0,0 +1,21 @@ +import { Component, input } from '@angular/core'; +import clsx from 'clsx'; + +/** + * Button group displays multiple related actions side-by-side. + */ +@Component({ + selector: 'app-button-group', + standalone: true, + template: ` +
+ +
+ `, +}) +export class ButtonGroupComponent { + readonly class = input(''); + + protected readonly classes = () => + clsx('flex flex-wrap gap-6 px-8 lg:gap-8 lg:px-10', this.class()); +} diff --git a/playgrounds/angular/src/components/checkbox.component.ts b/playgrounds/angular/src/components/checkbox.component.ts new file mode 100644 index 00000000..b5b3cd92 --- /dev/null +++ b/playgrounds/angular/src/components/checkbox.component.ts @@ -0,0 +1,54 @@ +import { Component, input, output } from '@angular/core'; +import clsx from 'clsx'; +import { InputErrorsComponent } from './input-errors.component.ts'; + +/** + * Checkbox that allows users to select an option. The label next to the + * checkbox describes the selection option. + */ +@Component({ + selector: 'app-checkbox', + standalone: true, + imports: [InputErrorsComponent], + template: ` +
+ + +
+ `, +}) +export class CheckboxComponent { + readonly name = input.required(); + readonly label = input(''); + readonly value = input(); + readonly input = input(); + readonly required = input(false); + readonly errors = input<[string, ...string[]] | null>(null); + readonly class = input(''); + + readonly fieldFocus = output(); + readonly fieldChange = output(); + readonly fieldBlur = output(); + + protected readonly containerClasses = () => + clsx('px-8 lg:px-10', this.class()); +} diff --git a/playgrounds/angular/src/components/color-button.component.ts b/playgrounds/angular/src/components/color-button.component.ts new file mode 100644 index 00000000..751d5fbc --- /dev/null +++ b/playgrounds/angular/src/components/color-button.component.ts @@ -0,0 +1,40 @@ +import { Component, input, output } from '@angular/core'; +import clsx from 'clsx'; + +type Color = 'green' | 'yellow' | 'purple' | 'blue' | 'red'; + +/** + * Button with a specified color used for demo purposes in the playground. + */ +@Component({ + selector: 'app-color-button', + standalone: true, + template: ` + + `, +}) +export class ColorButtonComponent { + readonly type = input<'button' | 'submit'>('button'); + readonly color = input.required(); + readonly label = input.required(); + readonly width = input<'auto' | undefined>(undefined); + readonly clicked = output(); + + protected readonly classes = () => + clsx( + 'h-14 rounded-2xl border-2 bg-white px-5 font-medium md:h-16 md:text-lg lg:h-[70px] lg:px-6 lg:text-xl dark:bg-gray-900', + this.color() === 'green' && + 'border-emerald-600/20 text-emerald-600 hover:bg-emerald-600/10 dark:border-emerald-400/20 dark:text-emerald-400 dark:hover:bg-emerald-400/10', + this.color() === 'yellow' && + 'border-yellow-600/20 text-yellow-600 hover:bg-yellow-600/10 dark:border-amber-300/20 dark:text-amber-300 dark:hover:bg-amber-300/10', + this.color() === 'purple' && + 'border-purple-600/20 text-purple-600 hover:bg-purple-600/10 dark:border-purple-400/20 dark:text-purple-400 dark:hover:bg-purple-400/10', + this.color() === 'blue' && + 'border-sky-600/20 text-sky-600 hover:bg-sky-600/10 dark:border-sky-400/20 dark:text-sky-400 dark:hover:bg-sky-400/10', + this.color() === 'red' && + 'border-red-600/20 text-red-600 hover:bg-red-600/10 dark:border-red-400/20 dark:text-red-400 dark:hover:bg-red-400/10', + this.width() !== 'auto' && 'w-full md:w-auto' + ); +} diff --git a/playgrounds/angular/src/components/expandable.component.ts b/playgrounds/angular/src/components/expandable.component.ts new file mode 100644 index 00000000..a64a9b95 --- /dev/null +++ b/playgrounds/angular/src/components/expandable.component.ts @@ -0,0 +1,56 @@ +import { + afterRenderEffect, + Component, + computed, + ElementRef, + HostListener, + input, + viewChild, +} from '@angular/core'; +import clsx from 'clsx'; + +/** + * Wrapper component to vertically expand or collapse content. + */ +@Component({ + selector: 'app-expandable', + standalone: true, + template: ` +
+ +
+ `, +}) +export class ExpandableComponent { + readonly expanded = input.required(); + + private readonly el = viewChild>('el'); + + protected readonly classes = computed(() => + clsx( + 'm-0! h-0 origin-top overflow-hidden duration-200', + !this.expanded() && 'invisible -translate-y-2 scale-y-75 opacity-0' + ) + ); + + constructor() { + afterRenderEffect(() => { + this.updateHeight(this.expanded()); + }); + } + + @HostListener('window:resize') + onResize(): void { + this.updateHeight(this.expanded()); + } + + private updateHeight(expanded: boolean): void { + const el = this.el()?.nativeElement; + if (!el) return; + el.style.height = expanded ? `${el.scrollHeight}px` : '0'; + } +} diff --git a/playgrounds/angular/src/components/file-input.component.ts b/playgrounds/angular/src/components/file-input.component.ts new file mode 100644 index 00000000..4c9168b2 --- /dev/null +++ b/playgrounds/angular/src/components/file-input.component.ts @@ -0,0 +1,77 @@ +import { Component, computed, input, output } from '@angular/core'; +import clsx from 'clsx'; +import { InputLabelComponent } from './input-label.component.ts'; +import { InputErrorsComponent } from './input-errors.component.ts'; + +/** + * File input field that users can click or drag files into. Various + * decorations can be displayed in or around the field to communicate the entry + * requirements. + */ +@Component({ + selector: 'app-file-input', + standalone: true, + imports: [InputLabelComponent, InputErrorsComponent], + template: ` +
+ + + +
+ `, +}) +export class FileInputComponent { + readonly name = input.required(); + readonly label = input(); + readonly accept = input(); + readonly multiple = input(false); + readonly required = input(false); + readonly input = input(); + readonly errors = input<[string, ...string[]] | null>(null); + readonly class = input(''); + + readonly fieldFocus = output(); + readonly fieldChange = output(); + readonly fieldBlur = output(); + + protected readonly files = computed(() => { + const val = this.input(); + if (!val) return []; + return Array.isArray(val) ? val : [val]; + }); + + protected readonly displayText = computed(() => { + const files = this.files(); + if (files.length) { + return `Selected file${this.multiple() ? 's' : ''}: ${files.map((f) => f.name).join(', ')}`; + } + return `Click or drag and drop file${this.multiple() ? 's' : ''}`; + }); + + protected readonly containerClasses = () => + clsx('px-8 lg:px-10', this.class()); + + protected readonly labelClasses = computed(() => + clsx( + 'relative flex min-h-24 w-full items-center justify-center rounded-2xl border-[3px] border-dashed border-slate-200 p-8 text-center focus-within:border-sky-600/50 hover:border-slate-300 md:min-h-28 md:text-lg lg:min-h-32 lg:p-10 lg:text-xl dark:border-slate-800 dark:focus-within:border-sky-400/50 dark:hover:border-slate-700', + !this.files().length && 'text-slate-500' + ) + ); +} diff --git a/playgrounds/angular/src/components/form-error.component.ts b/playgrounds/angular/src/components/form-error.component.ts new file mode 100644 index 00000000..b0f9f759 --- /dev/null +++ b/playgrounds/angular/src/components/form-error.component.ts @@ -0,0 +1,31 @@ +import { Component, input } from '@angular/core'; +import clsx from 'clsx'; +import { type FormStore } from '@formisch/angular'; +import { ExpandableComponent } from './expandable.component.ts'; + +/** + * Error component usually used at the end of a form to provide feedback to the + * user. + */ +@Component({ + selector: 'app-form-error', + standalone: true, + imports: [ExpandableComponent], + template: ` + +
+ {{ of().errors()?.[0] }} +
+
+ `, +}) +export class FormErrorComponent { + readonly of = input.required(); + readonly class = input(''); + + protected readonly classes = () => + clsx( + 'px-8 text-red-500 md:text-lg lg:px-10 lg:text-xl dark:text-red-400', + this.class() + ); +} diff --git a/playgrounds/angular/src/components/form-footer.component.ts b/playgrounds/angular/src/components/form-footer.component.ts index fcdd7202..3aeaf04d 100644 --- a/playgrounds/angular/src/components/form-footer.component.ts +++ b/playgrounds/angular/src/components/form-footer.component.ts @@ -1,24 +1,23 @@ import { Component, input } from '@angular/core'; import { reset, type FormStore } from '@formisch/angular'; +import { ActionButtonComponent } from './action-button.component.ts'; +/** + * Form footer with buttons to reset and submit the form. + */ @Component({ selector: 'app-form-footer', standalone: true, + imports: [ActionButtonComponent], template: `
- - + (clicked)="handleReset()" + />
`, }) diff --git a/playgrounds/angular/src/components/form-header.component.ts b/playgrounds/angular/src/components/form-header.component.ts index 641bd2f2..283a9777 100644 --- a/playgrounds/angular/src/components/form-header.component.ts +++ b/playgrounds/angular/src/components/form-header.component.ts @@ -1,28 +1,27 @@ import { Component, input } from '@angular/core'; import { reset, type FormStore } from '@formisch/angular'; +import { ActionButtonComponent } from './action-button.component.ts'; +/** + * Form header with heading and buttons to reset and submit the form. + */ @Component({ selector: 'app-form-header', standalone: true, + imports: [ActionButtonComponent], template: `

{{ heading() }}

`, diff --git a/playgrounds/angular/src/components/input-errors.component.ts b/playgrounds/angular/src/components/input-errors.component.ts new file mode 100644 index 00000000..66beb91a --- /dev/null +++ b/playgrounds/angular/src/components/input-errors.component.ts @@ -0,0 +1,25 @@ +import { Component, input } from '@angular/core'; +import { ExpandableComponent } from './expandable.component.ts'; + +/** + * Input error that tells the user what to do to fix the problem. + */ +@Component({ + selector: 'app-input-errors', + standalone: true, + imports: [ExpandableComponent], + template: ` + +
+ {{ errors()?.[0] }} +
+
+ `, +}) +export class InputErrorsComponent { + readonly name = input.required(); + readonly errors = input<[string, ...string[]] | null>(null); +} diff --git a/playgrounds/angular/src/components/input-label.component.ts b/playgrounds/angular/src/components/input-label.component.ts new file mode 100644 index 00000000..e3b0ac54 --- /dev/null +++ b/playgrounds/angular/src/components/input-label.component.ts @@ -0,0 +1,50 @@ +import { Component, input } from '@angular/core'; +import clsx from 'clsx'; + +/** + * Input label for a form field. + */ +@Component({ + selector: 'app-input-label', + standalone: true, + template: ` + @if (label()) { + @let labelClass = labelClasses(); + @if (component() === 'legend') { + + {{ label() }} + @if (required()) { + * + } + + } @else if (name()) { + + } @else { +
+ {{ label() }} + @if (required()) { + * + } +
+ } + } + `, +}) +export class InputLabelComponent { + readonly component = input<'label' | 'legend' | 'div'>(); + readonly name = input(); + readonly label = input(); + readonly required = input(false); + readonly margin = input<'none'>(); + + protected readonly labelClasses = () => + clsx( + 'inline-block font-medium md:text-lg lg:text-xl', + this.margin() !== 'none' && 'mb-4 lg:mb-5' + ); +} diff --git a/playgrounds/angular/src/components/radio-group.component.ts b/playgrounds/angular/src/components/radio-group.component.ts new file mode 100644 index 00000000..16e93c79 --- /dev/null +++ b/playgrounds/angular/src/components/radio-group.component.ts @@ -0,0 +1,59 @@ +import { Component, input, output } from '@angular/core'; +import clsx from 'clsx'; +import { InputLabelComponent } from './input-label.component.ts'; +import { InputErrorsComponent } from './input-errors.component.ts'; +import { RadioComponent } from './radio.component.ts'; + +interface RadioOption { + label: string; + value: string; +} + +/** + * Radio group that allows users to select a single option from a list. + * Uses fieldset and legend for proper HTML semantics and accessibility. + */ +@Component({ + selector: 'app-radio-group', + standalone: true, + imports: [InputLabelComponent, InputErrorsComponent, RadioComponent], + template: ` +
+ +
+ @for (option of options(); track option.value) { + + } +
+ +
+ `, +}) +export class RadioGroupComponent { + readonly name = input.required(); + readonly label = input(); + readonly options = input.required(); + readonly required = input(false); + readonly input = input(); + readonly errors = input<[string, ...string[]] | null>(null); + readonly class = input(''); + + readonly fieldFocus = output(); + readonly fieldChange = output(); + readonly fieldBlur = output(); + + protected readonly containerClasses = () => + clsx('px-8 lg:px-10', this.class()); +} diff --git a/playgrounds/angular/src/components/radio.component.ts b/playgrounds/angular/src/components/radio.component.ts new file mode 100644 index 00000000..14986696 --- /dev/null +++ b/playgrounds/angular/src/components/radio.component.ts @@ -0,0 +1,34 @@ +import { Component, input, output } from '@angular/core'; + +/** + * Simple radio button input. Should be used inside a RadioGroup component. + */ +@Component({ + selector: 'app-radio', + standalone: true, + template: ` + + `, +}) +export class RadioComponent { + readonly name = input.required(); + readonly label = input.required(); + readonly value = input.required(); + readonly checked = input(false); + + readonly fieldFocus = output(); + readonly fieldChange = output(); + readonly fieldBlur = output(); +} diff --git a/playgrounds/angular/src/components/select.component.ts b/playgrounds/angular/src/components/select.component.ts new file mode 100644 index 00000000..f97792c9 --- /dev/null +++ b/playgrounds/angular/src/components/select.component.ts @@ -0,0 +1,95 @@ +import { Component, computed, input, output } from '@angular/core'; +import clsx from 'clsx'; +import { InputLabelComponent } from './input-label.component.ts'; +import { InputErrorsComponent } from './input-errors.component.ts'; +import { AngleDownIconComponent } from '../icons/angle-down-icon.component.ts'; + +interface SelectOption { + label: string; + value: string; +} + +/** + * Select field that allows users to select predefined values. Various + * decorations can be displayed in or around the field to communicate the + * entry requirements. + */ +@Component({ + selector: 'app-select', + standalone: true, + imports: [InputLabelComponent, InputErrorsComponent, AngleDownIconComponent], + template: ` +
+ +
+ + @if (!multiple()) { + + } +
+ +
+ `, +}) +export class SelectComponent { + readonly name = input.required(); + readonly label = input(); + readonly options = input.required(); + readonly multiple = input(false); + readonly size = input(); + readonly placeholder = input(); + readonly required = input(false); + readonly input = input(); + readonly errors = input<[string, ...string[]] | null>(null); + readonly class = input(''); + + readonly fieldFocus = output(); + readonly fieldChange = output(); + readonly fieldBlur = output(); + + protected readonly values = computed(() => { + const val = this.input(); + if (Array.isArray(val)) return val; + if (val && typeof val === 'string') return [val]; + return []; + }); + + protected isSelected(value: string): boolean { + return this.values().includes(value); + } + + protected readonly containerClasses = () => + clsx('px-8 lg:px-10', this.class()); + + protected readonly selectClasses = computed(() => + clsx( + 'w-full appearance-none space-y-2 rounded-2xl border-2 bg-transparent px-5 outline-none md:text-lg lg:space-y-3 lg:px-6 lg:text-xl', + this.errors() + ? 'border-red-600/50 dark:border-red-400/50' + : 'border-slate-200 hover:border-slate-300 focus:border-sky-600/50 dark:border-slate-800 dark:hover:border-slate-700 dark:focus:border-sky-400/50', + this.multiple() ? 'py-5' : 'h-14 md:h-16 lg:h-[70px]', + this.placeholder() && !this.values().length && 'text-slate-500' + ) + ); +} diff --git a/playgrounds/angular/src/components/slider.component.ts b/playgrounds/angular/src/components/slider.component.ts new file mode 100644 index 00000000..26ef0805 --- /dev/null +++ b/playgrounds/angular/src/components/slider.component.ts @@ -0,0 +1,54 @@ +import { Component, input, output } from '@angular/core'; +import clsx from 'clsx'; +import { InputLabelComponent } from './input-label.component.ts'; +import { InputErrorsComponent } from './input-errors.component.ts'; + +/** + * Range slider that allows users to select predefined numbers. Various + * decorations can be displayed in or around the field to communicate the + * entry requirements. + */ +@Component({ + selector: 'app-slider', + standalone: true, + imports: [InputLabelComponent, InputErrorsComponent], + template: ` +
+ + + +
+ `, +}) +export class SliderComponent { + readonly name = input.required(); + readonly label = input(); + readonly min = input(); + readonly max = input(); + readonly step = input(); + readonly required = input(false); + readonly input = input(); + readonly errors = input<[string, ...string[]] | null>(null); + readonly class = input(''); + + readonly fieldFocus = output(); + readonly fieldChange = output(); + readonly fieldBlur = output(); + + protected readonly containerClasses = () => + clsx('px-8 lg:px-10', this.class()); +} diff --git a/playgrounds/angular/src/components/spinner.component.ts b/playgrounds/angular/src/components/spinner.component.ts new file mode 100644 index 00000000..64bf10fa --- /dev/null +++ b/playgrounds/angular/src/components/spinner.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; + +/** + * Spinner provides a visual cue that an action is being processed. + */ +@Component({ + selector: 'app-spinner', + standalone: true, + template: ` + + `, +}) +export class SpinnerComponent {} diff --git a/playgrounds/angular/src/components/text-input.component.ts b/playgrounds/angular/src/components/text-input.component.ts index 0b335b17..541aa0e6 100644 --- a/playgrounds/angular/src/components/text-input.component.ts +++ b/playgrounds/angular/src/components/text-input.component.ts @@ -1,22 +1,19 @@ import { Component, computed, input, output } from '@angular/core'; import clsx from 'clsx'; +import { InputLabelComponent } from './input-label.component.ts'; +import { InputErrorsComponent } from './input-errors.component.ts'; +/** + * Text input field that users can type into. Various decorations can be + * displayed in or around the field to communicate the entry requirements. + */ @Component({ selector: 'app-text-input', standalone: true, + imports: [InputLabelComponent, InputErrorsComponent], template: ` -
- @if (label()) { - - } +
+ - @if (errors()) { -
- {{ errors()![0] }} -
- } +
`, }) @@ -49,11 +39,15 @@ export class TextInputComponent { readonly required = input(false); readonly value = input(); readonly errors = input<[string, ...string[]] | null>(null); + readonly class = input(''); readonly fieldFocus = output(); readonly fieldChange = output(); readonly fieldBlur = output(); + protected readonly containerClasses = () => + clsx('px-8 lg:px-10', this.class()); + protected readonly inputClass = computed(() => clsx( 'h-14 w-full rounded-2xl border-2 bg-white px-5 outline-none placeholder:text-slate-500 md:h-16 md:text-lg lg:h-[70px] lg:px-6 lg:text-xl dark:bg-gray-900', diff --git a/playgrounds/angular/src/components/unstyled-button.component.ts b/playgrounds/angular/src/components/unstyled-button.component.ts new file mode 100644 index 00000000..72092724 --- /dev/null +++ b/playgrounds/angular/src/components/unstyled-button.component.ts @@ -0,0 +1,60 @@ +import { Component, input, output, signal } from '@angular/core'; +import clsx from 'clsx'; +import { SpinnerComponent } from './spinner.component.ts'; + +/** + * Basic button component with loading state and spinner animation. + */ +@Component({ + selector: 'app-unstyled-button', + standalone: true, + imports: [SpinnerComponent], + template: ` + + `, +}) +export class UnstyledButtonComponent { + readonly type = input<'button' | 'submit' | 'reset'>('button'); + readonly class = input(''); + readonly loading = input(false); + readonly onClick = input<(() => unknown) | undefined>(undefined); + readonly clicked = output(); + + protected readonly isLoading = signal(false); + protected readonly clsx = clsx; + + protected async handleClick(): Promise { + const handler = this.onClick(); + this.clicked.emit(); + if (!handler) return; + this.isLoading.set(true); + await handler(); + this.isLoading.set(false); + } +} diff --git a/playgrounds/angular/src/icons/angle-down-icon.component.ts b/playgrounds/angular/src/icons/angle-down-icon.component.ts new file mode 100644 index 00000000..df4548ce --- /dev/null +++ b/playgrounds/angular/src/icons/angle-down-icon.component.ts @@ -0,0 +1,24 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'app-angle-down-icon', + standalone: true, + template: ` + + + + `, +}) +export class AngleDownIconComponent { + readonly class = input(''); +} diff --git a/playgrounds/angular/src/routes/nested/nested.component.ts b/playgrounds/angular/src/routes/nested/nested.component.ts new file mode 100644 index 00000000..24393c5f --- /dev/null +++ b/playgrounds/angular/src/routes/nested/nested.component.ts @@ -0,0 +1,220 @@ +import { Component } from '@angular/core'; +import * as v from 'valibot'; +import { + FormischForm, + FormischField, + FormischFieldArray, + insert, + move, + remove, + replace, + swap, + injectForm, +} from '@formisch/angular'; +import { AutoAnimateDirective } from '../../components/auto-animate.directive.ts'; +import { FormHeaderComponent } from '../../components/form-header.component.ts'; +import { FormFooterComponent } from '../../components/form-footer.component.ts'; +import { TextInputComponent } from '../../components/text-input.component.ts'; +import { ColorButtonComponent } from '../../components/color-button.component.ts'; + +const NestedFormSchema = v.object({ + items: v.array( + v.object({ + label: v.optional(v.string()), + options: v.array(v.optional(v.string())), + }) + ), +}); + +@Component({ + selector: 'app-nested', + standalone: true, + imports: [ + FormischForm, + FormischField, + FormischFieldArray, + AutoAnimateDirective, + FormHeaderComponent, + FormFooterComponent, + TextInputComponent, + ColorButtonComponent, + ], + template: ` + + + + + +
+
+ @for (item of fieldArray.items(); track item; let itemIndex = $index) { +
+
+ + + + + + + +
+ + + + + +
+ @for (opt of optionsArray.items(); track opt; let optionIndex = $index) { +
+ + + + + + + +
+ } + +
+ + + +
+
+
+
+
+ } +
+ +
+ + + + +
+
+
+
+ + +
+ `, +}) +export class NestedComponent { + readonly form = injectForm({ + schema: NestedFormSchema, + initialInput: { + items: [ + { label: 'Item 1', options: ['Option 1', 'Option 2'] }, + { label: 'Item 2', options: ['Option 1', 'Option 2'] }, + ], + }, + }); + + readonly handleSubmit = (output: v.InferOutput) => { + console.log(output); + }; + + addItem(): void { + insert(this.form, { path: ['items'], initialInput: { label: '', options: [''] } }); + } + + moveItemFirstToEnd(length: number): void { + move(this.form, { path: ['items'], from: 0, to: length - 1 }); + } + + swapItemsFirstTwo(): void { + swap(this.form, { path: ['items'], at: 0, and: 1 }); + } + + replaceFirst(): void { + replace(this.form, { path: ['items'], at: 0, initialInput: { label: '', options: [''] } }); + } + + removeItem(index: number): void { + remove(this.form, { path: ['items'], at: index }); + } + + addOption(itemIndex: number): void { + insert(this.form, { path: ['items', itemIndex, 'options'], initialInput: '' }); + } + + moveOptionFirstToEnd(itemIndex: number, length: number): void { + move(this.form, { path: ['items', itemIndex, 'options'], from: 0, to: length - 1 }); + } + + swapOptionsFirstTwo(itemIndex: number): void { + swap(this.form, { path: ['items', itemIndex, 'options'], at: 0, and: 1 }); + } + + removeOption(itemIndex: number, optionIndex: number): void { + remove(this.form, { path: ['items', itemIndex, 'options'], at: optionIndex }); + } +} diff --git a/playgrounds/angular/src/routes/special/special.component.ts b/playgrounds/angular/src/routes/special/special.component.ts new file mode 100644 index 00000000..ef76e314 --- /dev/null +++ b/playgrounds/angular/src/routes/special/special.component.ts @@ -0,0 +1,226 @@ +import { Component } from '@angular/core'; +import * as v from 'valibot'; +import { FormischForm, FormischField, injectForm } from '@formisch/angular'; +import { FormHeaderComponent } from '../../components/form-header.component.ts'; +import { FormFooterComponent } from '../../components/form-footer.component.ts'; +import { TextInputComponent } from '../../components/text-input.component.ts'; +import { SliderComponent } from '../../components/slider.component.ts'; +import { CheckboxComponent } from '../../components/checkbox.component.ts'; +import { RadioGroupComponent } from '../../components/radio-group.component.ts'; +import { SelectComponent } from '../../components/select.component.ts'; +import { FileInputComponent } from '../../components/file-input.component.ts'; + +const SpecialFormSchema = v.object({ + number: v.optional(v.string()), + range: v.optional(v.string(), '50'), + checkbox: v.object({ + array: v.array(v.string()), + boolean: v.optional(v.boolean(), false), + }), + radio: v.optional(v.string()), + select: v.object({ + array: v.array(v.string()), + string: v.optional(v.string()), + }), + file: v.object({ + list: v.array(v.file()), + item: v.optional(v.file()), + }), +}); + +@Component({ + selector: 'app-special', + standalone: true, + imports: [ + FormischForm, + FormischField, + FormHeaderComponent, + FormFooterComponent, + TextInputComponent, + SliderComponent, + CheckboxComponent, + RadioGroupComponent, + SelectComponent, + FileInputComponent, + ], + template: ` + + + +
+ + + + + + + + + + + + + + +
+ @for (option of checkboxOptions; track option.value) { + + + + + + } +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ `, +}) +export class SpecialComponent { + readonly form = injectForm({ schema: SpecialFormSchema }); + + readonly checkboxOptions = [ + { label: 'Option 1', value: 'option_1' }, + { label: 'Option 2', value: 'option_2' }, + { label: 'Option 3', value: 'option_3' }, + ]; + + readonly radioOptions = [ + { label: 'Option 1', value: 'option_1' }, + { label: 'Option 2', value: 'option_2' }, + { label: 'Option 3', value: 'option_3' }, + ]; + + readonly selectOptions = [ + { label: 'Option 1', value: 'option_1' }, + { label: 'Option 2', value: 'option_2' }, + { label: 'Option 3', value: 'option_3' }, + ]; + + readonly handleSubmit = (output: v.InferOutput) => { + console.log(output); + }; +} diff --git a/playgrounds/angular/src/routes/todos/todos.component.ts b/playgrounds/angular/src/routes/todos/todos.component.ts index adb1ff88..798ef989 100644 --- a/playgrounds/angular/src/routes/todos/todos.component.ts +++ b/playgrounds/angular/src/routes/todos/todos.component.ts @@ -5,12 +5,19 @@ import { FormischField, FormischFieldArray, insert, + move, remove, + replace, + swap, injectForm, } from '@formisch/angular'; +import { AutoAnimateDirective } from '../../components/auto-animate.directive.ts'; import { FormHeaderComponent } from '../../components/form-header.component.ts'; import { FormFooterComponent } from '../../components/form-footer.component.ts'; import { TextInputComponent } from '../../components/text-input.component.ts'; +import { InputLabelComponent } from '../../components/input-label.component.ts'; +import { InputErrorsComponent } from '../../components/input-errors.component.ts'; +import { ColorButtonComponent } from '../../components/color-button.component.ts'; const TodosSchema = v.object({ heading: v.pipe( @@ -42,9 +49,13 @@ const TodosSchema = v.object({ FormischForm, FormischField, FormischFieldArray, + AutoAnimateDirective, FormHeaderComponent, FormFooterComponent, TextInputComponent, + InputLabelComponent, + InputErrorsComponent, + ColorButtonComponent, ], template: ` -
- +
+ - @if (fieldArray.errors()) { -
- {{ fieldArray.errors()![0] }} -
- } - - @for (item of fieldArray.items(); track item; let i = $index) { -
-
- - - - - +
+
+ @for (item of fieldArray.items(); track item; let index = $index) { +
+ + + + + - - - - - -
+ + + + + - + +
+ }
- } + +
- +
+ + + + +
@@ -169,4 +189,23 @@ export class TodosComponent { handleRemove(index: number): void { remove(this.form, { path: ['todos'], at: index }); } + + handleMoveFirstToEnd(length: number): void { + move(this.form, { path: ['todos'], from: 0, to: length - 1 }); + } + + handleSwapFirstTwo(): void { + swap(this.form, { path: ['todos'], at: 0, and: 1 }); + } + + handleReplaceFirst(): void { + replace(this.form, { + path: ['todos'], + at: 0, + initialInput: { + label: Math.random().toString(36).slice(2), + deadline: new Date().toISOString().split('T')[0], + }, + }); + } } diff --git a/playgrounds/angular/src/utils/disable-transitions.ts b/playgrounds/angular/src/utils/disable-transitions.ts new file mode 100644 index 00000000..ec61732e --- /dev/null +++ b/playgrounds/angular/src/utils/disable-transitions.ts @@ -0,0 +1,8 @@ +/** + * Disables CSS transitions for a short moment. + */ +export function disableTransitions(): void { + const { classList } = document.documentElement; + classList.add('disable-transitions'); + setTimeout(() => classList.remove('disable-transitions'), 250); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c36561bd..d8057fb1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -588,6 +588,9 @@ importers: '@formisch/angular': specifier: workspace:* version: link:../../frameworks/angular + '@formkit/auto-animate': + specifier: ^0.8.4 + version: 0.8.4 '@tailwindcss/vite': specifier: ^4.1.17 version: 4.1.17(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.85.0)(stylus@0.62.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) From f035c2dc56043aaa00c9d939cc86f83047645923 Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Fri, 29 May 2026 16:15:44 -0400 Subject: [PATCH 17/18] Fix select value binding and unstyled button loading state reset - Bind [value] on {{ placeholder() }} } @for (option of options(); track option.value) { - } @@ -75,10 +76,6 @@ export class SelectComponent { return []; }); - protected isSelected(value: string): boolean { - return this.values().includes(value); - } - protected readonly containerClasses = () => clsx('px-8 lg:px-10', this.class()); diff --git a/playgrounds/angular/src/components/unstyled-button.component.ts b/playgrounds/angular/src/components/unstyled-button.component.ts index 72092724..dde3c083 100644 --- a/playgrounds/angular/src/components/unstyled-button.component.ts +++ b/playgrounds/angular/src/components/unstyled-button.component.ts @@ -54,7 +54,10 @@ export class UnstyledButtonComponent { this.clicked.emit(); if (!handler) return; this.isLoading.set(true); - await handler(); - this.isLoading.set(false); + try { + await handler(); + } finally { + this.isLoading.set(false); + } } } From 66580bcbc7b2b19df449578e4418f531c0a0d5bd Mon Sep 17 00:00:00 2001 From: Sonu Kapoor Date: Sat, 30 May 2026 17:31:44 -0400 Subject: [PATCH 18/18] Refactor FormischField and FormischFieldArray to reuse inject functions Add signal-based overloads to injectField and injectFieldArray so they can accept InputSignal and InputSignal directly. FormischField and FormischFieldArray become thin wrappers that delegate to the inject functions, matching the pattern used in all other frameworks. --- .../components/FormischField/FormischField.ts | 110 +----------------- .../FormischFieldArray/FormischFieldArray.ts | 35 +----- .../src/functions/injectField/injectField.ts | 93 +++++++++++---- .../injectFieldArray/injectFieldArray.ts | 67 +++++++++-- 4 files changed, 131 insertions(+), 174 deletions(-) diff --git a/frameworks/angular/src/components/FormischField/FormischField.ts b/frameworks/angular/src/components/FormischField/FormischField.ts index 16f75e79..11474166 100644 --- a/frameworks/angular/src/components/FormischField/FormischField.ts +++ b/frameworks/angular/src/components/FormischField/FormischField.ts @@ -1,31 +1,20 @@ import { Component, - computed, contentChild, - DestroyRef, - inject, input, type InputSignal, type Signal, - TemplateRef + TemplateRef, } from '@angular/core'; import { NgTemplateOutlet } from '@angular/common'; import { - type FieldElement, - getElementInput, - getFieldBool, - getFieldInput, - getFieldStore, - INTERNAL, type RequiredPath, type Schema, - setFieldBool, - setFieldInput, - validateIfRequired, type ValidPath, } from '@formisch/core/angular'; import type * as v from 'valibot'; import type { FieldStore, FormStore } from '../../types/index.ts'; +import { injectField } from '../../functions/injectField/injectField.ts'; /** * Headless field component that provides reactive field state via an Angular template. @@ -37,7 +26,7 @@ import type { FieldStore, FormStore } from '../../types/index.ts'; * * * * @@ -54,7 +43,7 @@ import type { FieldStore, FormStore } from '../../types/index.ts'; [ngTemplateOutletContext]="{ $implicit: field }" /> } -`, + `, }) export class FormischField< TSchema extends Schema = Schema, @@ -64,94 +53,5 @@ export class FormischField< readonly path: InputSignal, TFieldPath>> = input.required, TFieldPath>>(); protected readonly template: Signal | undefined> = contentChild(TemplateRef); - - private readonly internalFormStore = computed(() => this.of()[INTERNAL]); - private readonly internalFieldStore = computed(() => - getFieldStore(this.internalFormStore(), this.path()) - ); - private readonly destroyRef = inject(DestroyRef); - - protected readonly field: FieldStore = (() => { - const internalFormStore = this.internalFormStore; - const internalFieldStore = this.internalFieldStore; - const pathSignal = this.path; - const destroyRef = this.destroyRef; - - destroyRef.onDestroy(() => { - internalFieldStore().elements = internalFieldStore().elements.filter( - (element) => element.isConnected - ); - }); - - return { - get path() { - return pathSignal(); - }, - input: computed(() => getFieldInput(internalFieldStore())), - errors: computed(() => internalFieldStore().errors.value), - isTouched: computed(() => - getFieldBool(internalFieldStore(), 'isTouched') - ), - isDirty: computed(() => getFieldBool(internalFieldStore(), 'isDirty')), - isValid: computed( - () => !getFieldBool(internalFieldStore(), 'errors') - ), - onInput(value) { - setFieldInput(internalFormStore(), pathSignal(), value); - validateIfRequired( - internalFormStore(), - internalFieldStore(), - 'input' - ); - }, - props: { - get name() { - return internalFieldStore().name; - }, - get autofocus() { - return !!internalFieldStore().errors.value; - }, - ref(element) { - if (element) { - internalFieldStore().elements.push(element); - } - }, - onFocus() { - setFieldBool(internalFieldStore(), 'isTouched', true); - validateIfRequired( - internalFormStore(), - internalFieldStore(), - 'touch' - ); - }, - onChange(event) { - setFieldInput( - internalFormStore(), - pathSignal(), - getElementInput( - event.currentTarget as FieldElement, - internalFieldStore() - ) - ); - validateIfRequired( - internalFormStore(), - internalFieldStore(), - 'input' - ); - validateIfRequired( - internalFormStore(), - internalFieldStore(), - 'change' - ); - }, - onBlur() { - validateIfRequired( - internalFormStore(), - internalFieldStore(), - 'blur' - ); - }, - }, - }; - })() as FieldStore; + protected readonly field: FieldStore = injectField(this.of, { path: this.path }); } diff --git a/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts index 9ec87ba8..0a4f09f1 100644 --- a/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts +++ b/frameworks/angular/src/components/FormischFieldArray/FormischFieldArray.ts @@ -1,6 +1,5 @@ import { Component, - computed, contentChild, input, type InputSignal, @@ -9,16 +8,13 @@ import { } from '@angular/core'; import { NgTemplateOutlet } from '@angular/common'; import { - getFieldBool, - getFieldStore, - INTERNAL, - type InternalArrayStore, type RequiredPath, type Schema, type ValidArrayPath, } from '@formisch/core/angular'; import type * as v from 'valibot'; import type { FieldArrayStore, FormStore } from '../../types/index.ts'; +import { injectFieldArray } from '../../functions/injectFieldArray/injectFieldArray.ts'; /** * Headless field array component that provides reactive field array state via an Angular template. @@ -56,32 +52,5 @@ export class FormischFieldArray< readonly path: InputSignal, TFieldArrayPath>> = input.required, TFieldArrayPath>>(); protected readonly template: Signal | undefined> = contentChild(TemplateRef); - - private readonly internalFieldStore = computed( - () => - getFieldStore(this.of()[INTERNAL], this.path()) as InternalArrayStore - ); - - protected readonly fieldArray: FieldArrayStore = - (() => { - const internalFieldStore = this.internalFieldStore; - const pathSignal = this.path; - - return { - get path() { - return pathSignal(); - }, - items: computed(() => internalFieldStore().items.value), - errors: computed(() => internalFieldStore().errors.value), - isTouched: computed(() => - getFieldBool(internalFieldStore(), 'isTouched') - ), - isDirty: computed(() => - getFieldBool(internalFieldStore(), 'isDirty') - ), - isValid: computed( - () => !getFieldBool(internalFieldStore(), 'errors') - ), - }; - })() as FieldArrayStore; + protected readonly fieldArray: FieldArrayStore = injectFieldArray(this.of, { path: this.path }); } diff --git a/frameworks/angular/src/functions/injectField/injectField.ts b/frameworks/angular/src/functions/injectField/injectField.ts index a7ba9bed..a7cd075e 100644 --- a/frameworks/angular/src/functions/injectField/injectField.ts +++ b/frameworks/angular/src/functions/injectField/injectField.ts @@ -3,6 +3,7 @@ import { computed, DestroyRef, inject, + type Signal, } from '@angular/core'; import { type FieldElement, @@ -34,6 +35,19 @@ export interface InjectFieldConfig< readonly path: ValidPath, TFieldPath>; } +/** + * Inject field config interface for signal-based inputs. + */ +export interface InjectFieldConfigSignal< + TSchema extends Schema = Schema, + TFieldPath extends RequiredPath = RequiredPath, +> { + /** + * A signal that returns the path to the field within the form schema. + */ + readonly path: Signal, TFieldPath>>; +} + /** * Creates a reactive field store for a specific field within a form store. * Exposes all reactive state as Angular Signals callable in templates. @@ -53,62 +67,93 @@ export function injectField< config: InjectFieldConfig ): FieldStore; +/** + * Creates a reactive field store for a specific field within a form store. + * Accepts signal inputs for use inside Angular components. + * + * Must be called in an injection context (component constructor or field initializer). + * + * @param form A signal returning the form store instance. + * @param config The field configuration with a signal path. + * + * @returns The field store with reactive Signal properties and element props. + */ +export function injectField< + TSchema extends Schema, + TFieldPath extends RequiredPath, +>( + form: Signal>, + config: InjectFieldConfigSignal +): FieldStore; + // @__NO_SIDE_EFFECTS__ export function injectField( - form: FormStore, - config: InjectFieldConfig + form: FormStore | Signal, + config: InjectFieldConfig | InjectFieldConfigSignal ): FieldStore { assertInInjectionContext(injectField); const destroyRef = inject(DestroyRef); - const internalFormStore = form[INTERNAL]; - const internalFieldStore = getFieldStore(internalFormStore, config.path); + const getForm: () => FormStore = + typeof form === 'function' ? form : () => form; + // Path may be a plain value or a signal — normalize to a zero-arg accessor. + const getPath: () => InjectFieldConfig['path'] = + typeof config.path === 'function' + ? config.path + : () => config.path as InjectFieldConfig['path']; + + const internalFormStore = computed(() => getForm()[INTERNAL]); + const internalFieldStore = computed(() => getFieldStore(internalFormStore(), getPath())); destroyRef.onDestroy(() => { - internalFieldStore.elements = internalFieldStore.elements.filter( + internalFieldStore().elements = internalFieldStore().elements.filter( (element) => element.isConnected ); }); return { - path: config.path, - input: computed(() => getFieldInput(internalFieldStore)), - errors: computed(() => internalFieldStore.errors.value), - isTouched: computed(() => getFieldBool(internalFieldStore, 'isTouched')), - isDirty: computed(() => getFieldBool(internalFieldStore, 'isDirty')), - isValid: computed(() => !getFieldBool(internalFieldStore, 'errors')), + get path() { + return getPath(); + }, + input: computed(() => getFieldInput(internalFieldStore())), + errors: computed(() => internalFieldStore().errors.value), + isTouched: computed(() => getFieldBool(internalFieldStore(), 'isTouched')), + isDirty: computed(() => getFieldBool(internalFieldStore(), 'isDirty')), + isValid: computed(() => !getFieldBool(internalFieldStore(), 'errors')), onInput(value) { - setFieldInput(internalFormStore, config.path, value); - validateIfRequired(internalFormStore, internalFieldStore, 'input'); + setFieldInput(internalFormStore(), getPath(), value); + validateIfRequired(internalFormStore(), internalFieldStore(), 'input'); }, props: { get name() { - return internalFieldStore.name; + return internalFieldStore().name; + }, + get autofocus() { + return !!internalFieldStore().errors.value; }, - autofocus: !!internalFieldStore.errors.value, ref(element) { if (element) { - internalFieldStore.elements.push(element); + internalFieldStore().elements.push(element); } }, onFocus() { - setFieldBool(internalFieldStore, 'isTouched', true); - validateIfRequired(internalFormStore, internalFieldStore, 'touch'); + setFieldBool(internalFieldStore(), 'isTouched', true); + validateIfRequired(internalFormStore(), internalFieldStore(), 'touch'); }, onChange(event) { setFieldInput( - internalFormStore, - config.path, + internalFormStore(), + getPath(), getElementInput( event.currentTarget as FieldElement, - internalFieldStore + internalFieldStore() ) ); - validateIfRequired(internalFormStore, internalFieldStore, 'input'); - validateIfRequired(internalFormStore, internalFieldStore, 'change'); + validateIfRequired(internalFormStore(), internalFieldStore(), 'input'); + validateIfRequired(internalFormStore(), internalFieldStore(), 'change'); }, onBlur() { - validateIfRequired(internalFormStore, internalFieldStore, 'blur'); + validateIfRequired(internalFormStore(), internalFieldStore(), 'blur'); }, }, }; diff --git a/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts index 44d517ae..494632a1 100644 --- a/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts +++ b/frameworks/angular/src/functions/injectFieldArray/injectFieldArray.ts @@ -1,6 +1,7 @@ import { assertInInjectionContext, computed, + type Signal, } from '@angular/core'; import { getFieldBool, @@ -27,6 +28,19 @@ export interface InjectFieldArrayConfig< readonly path: ValidArrayPath, TFieldArrayPath>; } +/** + * Inject field array config interface for signal-based inputs. + */ +export interface InjectFieldArrayConfigSignal< + TSchema extends Schema = Schema, + TFieldArrayPath extends RequiredPath = RequiredPath, +> { + /** + * A signal that returns the path to the field array within the form schema. + */ + readonly path: Signal, TFieldArrayPath>>; +} + /** * Creates a reactive field array store for a specific array field within a form store. * Exposes all reactive state as Angular Signals callable in templates. @@ -47,24 +61,53 @@ export function injectFieldArray< config: InjectFieldArrayConfig ): FieldArrayStore; +/** + * Creates a reactive field array store for a specific array field within a form store. + * Accepts signal inputs for use inside Angular components. + * + * Must be called in an injection context (component constructor or field initializer). + * + * @param form A signal returning the form store instance. + * @param config The field array configuration with a signal path. + * + * @returns The field array store with reactive Signal properties. + */ +export function injectFieldArray< + TSchema extends Schema, + TFieldArrayPath extends RequiredPath, +>( + form: Signal>, + config: InjectFieldArrayConfigSignal +): FieldArrayStore; + // @__NO_SIDE_EFFECTS__ export function injectFieldArray( - form: FormStore, - config: InjectFieldArrayConfig + form: FormStore | Signal, + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + config: InjectFieldArrayConfig | InjectFieldArrayConfigSignal ): FieldArrayStore { assertInInjectionContext(injectFieldArray); - const internalFieldStore = getFieldStore( - form[INTERNAL], - config.path - ) as InternalArrayStore; + const getForm: () => FormStore = + typeof form === 'function' ? form : () => form; + // Path may be a plain value or a signal — normalize to a zero-arg accessor. + const getPath: () => InjectFieldArrayConfig['path'] = + typeof config.path === 'function' + ? config.path + : () => config.path as InjectFieldArrayConfig['path']; + + const internalFieldStore = computed( + () => getFieldStore(getForm()[INTERNAL], getPath()) as InternalArrayStore + ); return { - path: config.path, - items: computed(() => internalFieldStore.items.value), - errors: computed(() => internalFieldStore.errors.value), - isTouched: computed(() => getFieldBool(internalFieldStore, 'isTouched')), - isDirty: computed(() => getFieldBool(internalFieldStore, 'isDirty')), - isValid: computed(() => !getFieldBool(internalFieldStore, 'errors')), + get path() { + return getPath(); + }, + items: computed(() => internalFieldStore().items.value), + errors: computed(() => internalFieldStore().errors.value), + isTouched: computed(() => getFieldBool(internalFieldStore(), 'isTouched')), + isDirty: computed(() => getFieldBool(internalFieldStore(), 'isDirty')), + isValid: computed(() => !getFieldBool(internalFieldStore(), 'errors')), }; }