diff --git a/examples/react/next-server-actions-zod/.gitignore b/examples/react/next-server-actions-zod/.gitignore new file mode 100644 index 000000000..fd3dbb571 --- /dev/null +++ b/examples/react/next-server-actions-zod/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/react/next-server-actions-zod/README.md b/examples/react/next-server-actions-zod/README.md new file mode 100644 index 000000000..c4033664f --- /dev/null +++ b/examples/react/next-server-actions-zod/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/react/next-server-actions-zod/next.config.mjs b/examples/react/next-server-actions-zod/next.config.mjs new file mode 100644 index 000000000..94be31c3d --- /dev/null +++ b/examples/react/next-server-actions-zod/next.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +} + +export default nextConfig diff --git a/examples/react/next-server-actions-zod/package.json b/examples/react/next-server-actions-zod/package.json new file mode 100644 index 000000000..1058090b0 --- /dev/null +++ b/examples/react/next-server-actions-zod/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/form-example-react-next-server-actions-zod", + "private": true, + "type": "module", + "scripts": { + "dev": "next dev", + "build": "next build", + "_test:types": "tsc" + }, + "dependencies": { + "@tanstack/react-form-nextjs": "^1.28.0", + "@tanstack/react-store": "^0.8.0", + "next": "16.0.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "zod": "^3.25.76" + }, + "devDependencies": { + "@types/node": "^24.1.0", + "@types/react": "^19.0.7", + "@types/react-dom": "^19.0.3", + "typescript": "5.8.2" + } +} diff --git a/examples/react/next-server-actions-zod/src/app/action.ts b/examples/react/next-server-actions-zod/src/app/action.ts new file mode 100644 index 000000000..76ce9e669 --- /dev/null +++ b/examples/react/next-server-actions-zod/src/app/action.ts @@ -0,0 +1,39 @@ +'use server' + +import { + ServerValidateError, + createServerValidate, +} from '@tanstack/react-form-nextjs' +import { z } from 'zod' +import { formOpts } from './shared-code' + +const schema = z.object({ + age: z.coerce.number().min(12), + firstName: z.string(), +}) + +const serverValidate = createServerValidate({ + ...formOpts, + onServerValidate: schema, +}) + +export default async function someAction(prev: unknown, formData: FormData) { + try { + const validatedData = await serverValidate(formData) + console.log('validatedData', validatedData) + // Persist the form data to the database + // await sql` + // INSERT INTO users (name, email, password) + // VALUES (${validatedData.name}, ${validatedData.email}, ${validatedData.password}) + // ` + } catch (e) { + if (e instanceof ServerValidateError) { + return e.formState + } + + // Some other error occurred while validating your form + throw e + } + + // Your form has successfully validated! +} diff --git a/examples/react/next-server-actions-zod/src/app/client-component.tsx b/examples/react/next-server-actions-zod/src/app/client-component.tsx new file mode 100644 index 000000000..d7511a649 --- /dev/null +++ b/examples/react/next-server-actions-zod/src/app/client-component.tsx @@ -0,0 +1,60 @@ +'use client' + +import { useActionState } from 'react' +import { + initialFormState, + mergeForm, + useForm, + useTransform, +} from '@tanstack/react-form-nextjs' +import { z } from 'zod' +import someAction from './action' +import { formOpts } from './shared-code' + +export const ClientComp = () => { + const [state, action] = useActionState(someAction, initialFormState) + + const form = useForm({ + ...formOpts, + transform: useTransform( + (baseForm) => mergeForm(baseForm, state ?? {}), + [state], + ), + }) + + return ( +
form.handleSubmit()}> + + {(field) => { + return ( +
+ field.handleChange(e.target.valueAsNumber)} + /> + {field.state.meta.errors.map((error) => ( +

{error?.message}

+ ))} +
+ ) + }} +
+ [formState.canSubmit, formState.isSubmitting]} + > + {([canSubmit, isSubmitting]) => ( + + )} + +
+ ) +} diff --git a/examples/react/next-server-actions-zod/src/app/layout.tsx b/examples/react/next-server-actions-zod/src/app/layout.tsx new file mode 100644 index 000000000..111474136 --- /dev/null +++ b/examples/react/next-server-actions-zod/src/app/layout.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/examples/react/next-server-actions-zod/src/app/page.tsx b/examples/react/next-server-actions-zod/src/app/page.tsx new file mode 100644 index 000000000..7d8c877c1 --- /dev/null +++ b/examples/react/next-server-actions-zod/src/app/page.tsx @@ -0,0 +1,9 @@ +import { ClientComp } from './client-component' + +export default function Home() { + return ( + <> + + + ) +} diff --git a/examples/react/next-server-actions-zod/src/app/shared-code.ts b/examples/react/next-server-actions-zod/src/app/shared-code.ts new file mode 100644 index 000000000..119363084 --- /dev/null +++ b/examples/react/next-server-actions-zod/src/app/shared-code.ts @@ -0,0 +1,8 @@ +import { formOptions } from '@tanstack/react-form-nextjs' + +export const formOpts = formOptions({ + defaultValues: { + firstName: '', + age: 0, + }, +}) diff --git a/examples/react/next-server-actions-zod/tsconfig.json b/examples/react/next-server-actions-zod/tsconfig.json new file mode 100644 index 000000000..4c2b4986c --- /dev/null +++ b/examples/react/next-server-actions-zod/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +} diff --git a/examples/react/next-server-actions/package.json b/examples/react/next-server-actions/package.json index 5e0a8f92f..45d8c99e0 100644 --- a/examples/react/next-server-actions/package.json +++ b/examples/react/next-server-actions/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/react-form-nextjs": "^1.28.0", "@tanstack/react-store": "^0.8.0", - "next": "15.5.3", + "next": "16.0.5", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/next-server-actions/tsconfig.json b/examples/react/next-server-actions/tsconfig.json index 28a97cedc..4c2b4986c 100644 --- a/examples/react/next-server-actions/tsconfig.json +++ b/examples/react/next-server-actions/tsconfig.json @@ -11,7 +11,7 @@ "moduleResolution": "Bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -19,6 +19,12 @@ } ] }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], "exclude": ["node_modules"] } diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index e7e9fb2ad..f1b5abde4 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -40,6 +40,7 @@ import type { FieldManipulator, FormValidationError, FormValidationErrorMap, + GlobalFormValidationError, ListenerCause, UpdateMetaOptions, ValidationCause, @@ -218,55 +219,6 @@ export interface FormValidators< onDynamicAsyncDebounceMs?: number } -/** - * @private - */ -export interface FormTransform< - TFormData, - TOnMount extends undefined | FormValidateOrFn, - TOnChange extends undefined | FormValidateOrFn, - TOnChangeAsync extends undefined | FormAsyncValidateOrFn, - TOnBlur extends undefined | FormValidateOrFn, - TOnBlurAsync extends undefined | FormAsyncValidateOrFn, - TOnSubmit extends undefined | FormValidateOrFn, - TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, - TOnDynamic extends undefined | FormValidateOrFn, - TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, - TOnServer extends undefined | FormAsyncValidateOrFn, - TSubmitMeta = never, -> { - fn: ( - formBase: FormApi< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta - >, - ) => FormApi< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta - > - deps: unknown[] -} - export interface FormListeners< TFormData, TOnMount extends undefined | FormValidateOrFn, @@ -498,20 +450,13 @@ export interface FormOptions< > meta: TSubmitMeta }) => void - transform?: FormTransform< - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer, - NoInfer - > + // Only runs in `core` for the first run on the Form, any additional transforms + // need to be handled at framework runtime due to complexity of comparison check + // and state merging + // + // Made intentionally type loose to avoid headaches with framework's individual + // `useTransform` hooks + transform?: (data: unknown) => unknown } export type AnyFormOptions = FormOptions< @@ -659,6 +604,20 @@ export type BaseFormState< _force_re_eval?: boolean } +export type AnyBaseFormState = BaseFormState< + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any +> + export type DerivedFormState< in out TFormData, in out TOnMount extends undefined | FormValidateOrFn, @@ -916,6 +875,7 @@ export class FormApi< TOnServer, TSubmitMeta > = {} + baseStore!: Store< BaseFormState< TFormData, @@ -970,11 +930,6 @@ export class FormApi< return this.store.state } - /** - * @private - */ - prevTransformArray: unknown[] = [] - /** * @private */ @@ -1021,13 +976,67 @@ export class FormApi< this._devtoolsSubmissionOverride = false - this.baseStore = new Store( - getDefaultFormState({ - ...(opts?.defaultState as any), - values: opts?.defaultValues ?? opts?.defaultState?.values, - isFormValid: true, - }), - ) + let baseStoreVal: BaseFormState< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer + > = getDefaultFormState({ + ...(opts?.defaultState as any), + values: opts?.defaultValues ?? opts?.defaultState?.values, + isFormValid: true, + }) + + if (opts?.transform) { + baseStoreVal = ( + opts.transform({ state: baseStoreVal }) as { state: unknown } + ).state as never + for (const errKey of Object.keys(baseStoreVal.errorMap)) { + const errKeyMap = baseStoreVal.errorMap[errKey as never] as + | GlobalFormValidationError + | undefined + if ( + errKeyMap === undefined || + !isGlobalFormValidationError(errKeyMap) + ) { + continue + } + + for (const fieldName of Object.keys(errKeyMap.fields)) { + const fieldErr = errKeyMap.fields[fieldName] + if (fieldErr === undefined) { + continue + } + const existingFieldMeta = baseStoreVal.fieldMetaBase[ + fieldName as never + ] as AnyFieldMetaBase | undefined + baseStoreVal.fieldMetaBase[fieldName as never] = { + isTouched: false, + isValidating: false, + isBlurred: false, + isDirty: false, + ...(existingFieldMeta ?? {}), + errorSourceMap: { + ...(existingFieldMeta?.['errorSourceMap'] ?? {}), + onChange: 'form', + }, + errorMap: { + ...(existingFieldMeta?.['errorMap'] ?? {}), + [errKey as never]: fieldErr, + }, + } satisfies AnyFieldMetaBase as never + } + } + } + + this.baseStore = new Store(baseStoreVal) this.fieldMetaDerived = new Derived({ deps: [this.baseStore], @@ -1260,7 +1269,7 @@ export class FormApi< return prevVal } - let state = { + const state = { ...currBaseStore, errorMap, fieldMeta: this.fieldMetaDerived.state, @@ -1289,20 +1298,6 @@ export class FormApi< TOnServer > - // Only run transform if state has shallowly changed - IE how React.useEffect works - const transformArray = this.options.transform?.deps ?? [] - const shouldTransform = - transformArray.length !== this.prevTransformArray.length || - transformArray.some((val, i) => val !== this.prevTransformArray[i]) - - if (shouldTransform) { - const newObj = Object.assign({}, this, { state }) - // This mutates the state - this.options.transform?.fn(newObj) - state = newObj.state - this.prevTransformArray = transformArray - } - return state }, }) @@ -1443,11 +1438,6 @@ export class FormApi< // Options need to be updated first so that when the store is updated, the state is correct for the derived state this.options = options - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - const shouldUpdateReeval = !!options.transform?.deps?.some( - (val, i) => val !== this.prevTransformArray[i], - ) - const shouldUpdateValues = options.defaultValues && !evaluate(options.defaultValues, oldOptions.defaultValues) && @@ -1457,7 +1447,7 @@ export class FormApi< !evaluate(options.defaultState, oldOptions.defaultState) && !this.state.isTouched - if (!shouldUpdateValues && !shouldUpdateState && !shouldUpdateReeval) return + if (!shouldUpdateValues && !shouldUpdateState) return batch(() => { this.baseStore.setState(() => @@ -1473,10 +1463,6 @@ export class FormApi< values: options.defaultValues, } : {}, - - shouldUpdateReeval - ? { _force_re_eval: !this.state._force_re_eval } - : {}, ), ), ) diff --git a/packages/form-core/src/index.ts b/packages/form-core/src/index.ts index 825eee5c6..94c0f3eea 100644 --- a/packages/form-core/src/index.ts +++ b/packages/form-core/src/index.ts @@ -9,3 +9,4 @@ export * from './standardSchemaValidator' export * from './FieldGroupApi' export * from './ValidationLogic' export * from './EventClient' +export * from './transform' diff --git a/packages/form-core/src/mergeForm.ts b/packages/form-core/src/mergeForm.ts index 9e43f0cab..e816e6754 100644 --- a/packages/form-core/src/mergeForm.ts +++ b/packages/form-core/src/mergeForm.ts @@ -1,4 +1,8 @@ -import type { FormApi } from './FormApi' +import type { + FormApi, + FormAsyncValidateOrFn, + FormValidateOrFn, +} from './FormApi' function isValidKey(key: string | number | symbol): boolean { const dangerousProps = ['__proto__', 'constructor', 'prototype'] @@ -70,35 +74,48 @@ export function mutateMergeDeep( return target } -export function mergeForm( +export function mergeForm< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta = never, +>( baseForm: FormApi< - NoInfer, - any, - any, - any, - any, - any, - any, - any, - any, - any, - any, - any + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta >, state: Partial< FormApi< TFormData, - any, - any, - any, - any, - any, - any, - any, - any, - any, - any, - any + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta >['state'] >, ) { diff --git a/packages/form-core/src/transform.ts b/packages/form-core/src/transform.ts new file mode 100644 index 000000000..d5f0276d6 --- /dev/null +++ b/packages/form-core/src/transform.ts @@ -0,0 +1,155 @@ +import { batch } from '@tanstack/store' +import { deepCopy } from './utils' +import type { + AnyBaseFormState, + FormApi, + FormAsyncValidateOrFn, + FormValidateOrFn, +} from './FormApi' + +/** + * @private + */ +export type FormTransform< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta = never, +> = ( + formBase: FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + >, +) => FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta +> + +/** + * @private + */ +export function mergeAndUpdate< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta = never, +>( + form: FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + >, + fn?: FormTransform< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + >, +) { + // Run the `transform` function on `form.state`, diff it, and update the relevant parts with what needs updating + if (!fn) return + + const newObj = Object.assign({}, form, { + state: deepCopy(form.state), + }) + + fn(newObj) + + if (newObj.fieldInfo !== form.fieldInfo) { + form.fieldInfo = newObj.fieldInfo + } + + if (newObj.options !== form.options) { + form.options = newObj.options + } + + const baseFormKeys = Object.keys({ + values: null, + validationMetaMap: null, + fieldMetaBase: null, + isSubmitting: null, + isSubmitted: null, + isValidating: null, + submissionAttempts: null, + isSubmitSuccessful: null, + _force_re_eval: null, + // Do not remove this, it ensures that we have all the keys in `BaseFormState` + } satisfies Record< + // Exclude errorMap since we need to handle that uniquely + Exclude, + null + >) as Array + + const diffedObject = baseFormKeys.reduce((prev, key) => { + if (form.state[key] !== newObj.state[key]) { + prev[key] = newObj.state[key] + } + return prev + }, {} as Partial) + + batch(() => { + if (Object.keys(diffedObject).length) { + form.baseStore.setState((prev) => ({ ...prev, ...diffedObject })) + } + + if (newObj.state.errorMap !== form.state.errorMap) { + // Check if we need to update `fieldMetaBase` with `errorMaps` set by + form.setErrorMap(newObj.state.errorMap) + } + }) + + return newObj +} diff --git a/packages/form-core/src/utils.ts b/packages/form-core/src/utils.ts index 788c7c14b..34cfdedf6 100644 --- a/packages/form-core/src/utils.ts +++ b/packages/form-core/src/utils.ts @@ -615,3 +615,47 @@ export const throttleFormState = liteThrottle( wait: 300, }, ) + +// Do not use a serialize and deserialize method like JSON.stringify/parse +// as that will drop functions, dates, undefined, Infinity, NaN, etc. +export function deepCopy(obj: T): T { + if (obj === null || typeof obj !== 'object') { + return obj + } + + if (obj instanceof Date) { + return new Date(obj.getTime()) as any + } + + if (obj instanceof Array) { + const arrCopy = [] as any[] + for (let i = 0; i < obj.length; i++) { + arrCopy[i] = deepCopy(obj[i]) + } + return arrCopy as any + } + + if (obj instanceof Map) { + const mapCopy = new Map() + obj.forEach((value, key) => { + mapCopy.set(key, deepCopy(value)) + }) + return mapCopy as any + } + + if (obj instanceof Set) { + const setCopy = new Set() + obj.forEach((value) => { + setCopy.add(deepCopy(value)) + }) + return setCopy as any + } + + const copy: { [key: string]: any } = {} + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + copy[key] = deepCopy((obj as any)[key]) + } + } + return copy as T +} diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index dd418d727..c1b36a85c 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -1,8 +1,13 @@ import { describe, expect, it, vi } from 'vitest' import { z } from 'zod' -import { FieldApi, FormApi, formEventClient } from '../src/index' +import { FieldApi, FormApi, formEventClient, mergeForm } from '../src/index' import { sleep } from './utils' -import type { AnyFieldApi, AnyFormApi } from '../src/index' +import type { + AnyFieldApi, + AnyFormApi, + AnyFormState, + FormState, +} from '../src/index' describe('form api', () => { it('should get default form state when default values are passed', () => { @@ -4113,3 +4118,69 @@ describe('form api event client', () => { logSpy.mockRestore() }) }) + +describe('form transform', () => { + it('should transform state values on first load', async () => { + const form = new FormApi({ + defaultValues: { + name: 'test', + }, + transform: (baseForm) => { + return mergeForm(baseForm as AnyFormApi, { + values: { + name: 'Another', + }, + }) + }, + }) + + form.mount() + + expect(form.state.values.name).toBe('Another') + }) + + it('should transform form error map on first load', async () => { + const form = new FormApi({ + defaultValues: { + name: 'test', + }, + transform: (baseForm) => { + return mergeForm(baseForm as AnyFormApi, { + errorMap: { + onChange: 'Error', + }, + }) + }, + }) + + form.mount() + + expect(form.state.errorMap.onChange).toBe('Error') + }) + + it('should transform fields error map on first load', async () => { + const form = new FormApi({ + defaultValues: { + name: 'test', + }, + transform: (baseForm) => { + return mergeForm(baseForm as AnyFormApi, { + errorMap: { + onChange: { fields: { name: 'Error' } }, + }, + }) + }, + }) + + form.mount() + + const field = new FieldApi({ + name: 'name', + form, + }) + + field.mount() + + expect(field.state.meta.errorMap.onChange).toBe('Error') + }) +}) diff --git a/packages/form-core/tests/transform.spec.ts b/packages/form-core/tests/transform.spec.ts new file mode 100644 index 000000000..f67b39352 --- /dev/null +++ b/packages/form-core/tests/transform.spec.ts @@ -0,0 +1,36 @@ +import { expect, it } from 'vitest' +import { FieldApi, FormApi, mergeAndUpdate, mergeForm } from '../src' +import type { AnyFormState } from '../src' + +it('transform option does not invalidate state for the field', () => { + const state: Partial = { + errorMap: { + onChange: { + fields: { + age: 'Age is required', + }, + }, + }, + } + + const form = new FormApi({ + defaultValues: { + age: 0, + }, + }) + + form.mount() + + const ageField = new FieldApi({ + form, + name: 'age', + }) + + ageField.mount() + + expect(ageField.state.meta.isValid).toBe(true) + + mergeAndUpdate(form, (f) => mergeForm(f, state)) + + expect(ageField.state.meta.isValid).toBe(false) +}) diff --git a/packages/react-form-nextjs/src/useTransform.ts b/packages/react-form-nextjs/src/useTransform.ts index 9bb9abfc8..9b5180d22 100644 --- a/packages/react-form-nextjs/src/useTransform.ts +++ b/packages/react-form-nextjs/src/useTransform.ts @@ -1,11 +1,7 @@ -import type { AnyFormApi, FormTransform } from '@tanstack/react-form' +import { useCallback } from 'react' +import type { AnyFormApi } from '@tanstack/react-form' -export function useTransform( +export const useTransform: ( fn: (formBase: AnyFormApi) => AnyFormApi, - deps: unknown[], -): FormTransform { - return { - fn, - deps, - } -} + deps?: unknown[], +) => (data: unknown) => unknown = useCallback as never diff --git a/packages/react-form-nextjs/tests/useTransform.spec.tsx b/packages/react-form-nextjs/tests/useTransform.spec.tsx new file mode 100644 index 000000000..350ef3ade --- /dev/null +++ b/packages/react-form-nextjs/tests/useTransform.spec.tsx @@ -0,0 +1,7 @@ +import { describe, expect, it } from 'vitest' + +describe('test suite', () => { + it('should work', () => { + expect(true).toBe(true) + }) +}) diff --git a/packages/react-form-remix/src/useTransform.ts b/packages/react-form-remix/src/useTransform.ts index 9bb9abfc8..9b5180d22 100644 --- a/packages/react-form-remix/src/useTransform.ts +++ b/packages/react-form-remix/src/useTransform.ts @@ -1,11 +1,7 @@ -import type { AnyFormApi, FormTransform } from '@tanstack/react-form' +import { useCallback } from 'react' +import type { AnyFormApi } from '@tanstack/react-form' -export function useTransform( +export const useTransform: ( fn: (formBase: AnyFormApi) => AnyFormApi, - deps: unknown[], -): FormTransform { - return { - fn, - deps, - } -} + deps?: unknown[], +) => (data: unknown) => unknown = useCallback as never diff --git a/packages/react-form-remix/tests/useTransform.spec.tsx b/packages/react-form-remix/tests/useTransform.spec.tsx new file mode 100644 index 000000000..350ef3ade --- /dev/null +++ b/packages/react-form-remix/tests/useTransform.spec.tsx @@ -0,0 +1,7 @@ +import { describe, expect, it } from 'vitest' + +describe('test suite', () => { + it('should work', () => { + expect(true).toBe(true) + }) +}) diff --git a/packages/react-form-start/src/useTransform.ts b/packages/react-form-start/src/useTransform.ts index 9bb9abfc8..9b5180d22 100644 --- a/packages/react-form-start/src/useTransform.ts +++ b/packages/react-form-start/src/useTransform.ts @@ -1,11 +1,7 @@ -import type { AnyFormApi, FormTransform } from '@tanstack/react-form' +import { useCallback } from 'react' +import type { AnyFormApi } from '@tanstack/react-form' -export function useTransform( +export const useTransform: ( fn: (formBase: AnyFormApi) => AnyFormApi, - deps: unknown[], -): FormTransform { - return { - fn, - deps, - } -} + deps?: unknown[], +) => (data: unknown) => unknown = useCallback as never diff --git a/packages/react-form-start/tests/useTransform.spec.tsx b/packages/react-form-start/tests/useTransform.spec.tsx new file mode 100644 index 000000000..350ef3ade --- /dev/null +++ b/packages/react-form-start/tests/useTransform.spec.tsx @@ -0,0 +1,7 @@ +import { describe, expect, it } from 'vitest' + +describe('test suite', () => { + it('should work', () => { + expect(true).toBe(true) + }) +}) diff --git a/packages/react-form/src/index.ts b/packages/react-form/src/index.ts index d58951cd2..e032da0e2 100644 --- a/packages/react-form/src/index.ts +++ b/packages/react-form/src/index.ts @@ -7,3 +7,4 @@ export * from './types' export * from './useField' export * from './useFieldGroup' export * from './useForm' +export * from './useIsomorphicLayoutEffect' diff --git a/packages/react-form/src/useForm.tsx b/packages/react-form/src/useForm.tsx index 525559675..b4ce691e6 100644 --- a/packages/react-form/src/useForm.tsx +++ b/packages/react-form/src/useForm.tsx @@ -1,8 +1,8 @@ 'use client' -import { FormApi, functionalUpdate } from '@tanstack/form-core' +import { FormApi, functionalUpdate, mergeAndUpdate } from '@tanstack/form-core' import { useStore } from '@tanstack/react-store' -import { useMemo, useState } from 'react' +import { useMemo, useRef, useState } from 'react' import { Field } from './useField' import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect' import { useFormId } from './useFormId' @@ -266,5 +266,17 @@ export function useForm< formApi.update(opts) }) + const hasRan = useRef(false) + + useIsomorphicLayoutEffect(() => { + if (!hasRan.current) return + if (!opts?.transform) return + mergeAndUpdate(formApi, opts.transform as never) + }, [formApi, opts?.transform]) + + useIsomorphicLayoutEffect(() => { + hasRan.current = true + }) + return extendedFormApi } diff --git a/packages/react-form/tests/useForm.test.tsx b/packages/react-form/tests/useForm.test.tsx index dbb7ff845..36daaaa51 100644 --- a/packages/react-form/tests/useForm.test.tsx +++ b/packages/react-form/tests/useForm.test.tsx @@ -2,8 +2,8 @@ import { describe, expect, it, vi } from 'vitest' import { render, waitFor } from '@testing-library/react' import { userEvent } from '@testing-library/user-event' import { useStore } from '@tanstack/react-store' -import { useEffect, useState } from 'react' -import { useForm } from '../src/index' +import { useCallback, useEffect, useReducer, useState } from 'react' +import { mergeForm, useForm } from '../src/index' import { sleep } from './utils' const user = userEvent.setup() @@ -1003,4 +1003,76 @@ describe('useForm', () => { await user.type(input, 'a') await user.click(removeButton) }) + + it('should handle stable transforms to update the base form on first render', async () => { + let renders = 0 + function Comp() { + const form = useForm({ + defaultValues: { + test: 'Hello', + }, + transform: useCallback((baseForm: unknown) => { + return mergeForm(baseForm as never, { + values: { + test: 'What', + }, + }) + }, []), + }) + + renders++ + + return ( + ( +

+ {field.state.value} {renders} +

+ )} + /> + ) + } + + const { getByText } = render() + getByText('What 1') + }) + + it('should handle stable transforms to update the base form on subsequent renders', async () => { + function Comp() { + const [renders, setRenders] = useState(0) + const form = useForm({ + defaultValues: { + test: 'Hello', + }, + transform: useCallback( + (baseForm: unknown) => { + return mergeForm(baseForm as never, { + values: { + test: renders === 0 ? 'First' : 'Another', + }, + }) + }, + [renders], + ), + }) + + return ( +
+

{field.state.value}

} + /> + +
+ ) + } + + const { findByText, getByText } = render() + await findByText('First') + await user.click(getByText('Rerender')) + await findByText('Another') + }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92c87132a..d6f87817c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -584,8 +584,8 @@ importers: specifier: ^0.8.0 version: 0.8.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next: - specifier: 15.5.3 - version: 15.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.90.0) + specifier: 16.0.5 + version: 16.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.90.0) react: specifier: ^19.0.0 version: 19.1.0 @@ -606,6 +606,40 @@ importers: specifier: 5.8.2 version: 5.8.2 + examples/react/next-server-actions-zod: + dependencies: + '@tanstack/react-form-nextjs': + specifier: ^1.28.0 + version: link:../../../packages/react-form-nextjs + '@tanstack/react-store': + specifier: ^0.8.0 + version: 0.8.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: + specifier: 16.0.5 + version: 16.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.90.0) + react: + specifier: ^19.0.0 + version: 19.1.0 + react-dom: + specifier: ^19.0.0 + version: 19.1.0(react@19.1.0) + zod: + specifier: ^3.25.76 + version: 3.25.76 + devDependencies: + '@types/node': + specifier: ^24.1.0 + version: 24.1.0 + '@types/react': + specifier: ^19.0.7 + version: 19.1.6 + '@types/react-dom': + specifier: ^19.0.3 + version: 19.1.5(@types/react@19.1.6) + typescript: + specifier: 5.8.2 + version: 5.8.2 + examples/react/query-integration: dependencies: '@tanstack/react-devtools': @@ -3125,124 +3159,139 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@img/sharp-darwin-arm64@0.34.3': - resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.3': - resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.0': - resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.0': - resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.0': - resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.0': - resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.0': - resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.0': - resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.0': - resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': - resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.0': - resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.3': - resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.3': - resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.3': - resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - '@img/sharp-linux-s390x@0.34.3': - resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.3': - resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.3': - resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.3': - resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.3': - resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.3': - resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.3': - resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.3': - resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -3776,53 +3825,53 @@ packages: '@napi-rs/wasm-runtime@1.1.0': resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} - '@next/env@15.5.3': - resolution: {integrity: sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==} + '@next/env@16.0.5': + resolution: {integrity: sha512-jRLOw822AE6aaIm9oh0NrauZEM0Vtx5xhYPgqx89txUmv/UmcRwpcXmGeQOvYNT/1bakUwA+nG5CA74upYVVDw==} - '@next/swc-darwin-arm64@15.5.3': - resolution: {integrity: sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==} + '@next/swc-darwin-arm64@16.0.5': + resolution: {integrity: sha512-65Mfo1rD+mVbJuBTlXbNelNOJ5ef+5pskifpFHsUt3cnOWjDNKctHBwwSz9tJlPp7qADZtiN/sdcG7mnc0El8Q==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.5.3': - resolution: {integrity: sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==} + '@next/swc-darwin-x64@16.0.5': + resolution: {integrity: sha512-2fDzXD/JpEjY500VUF0uuGq3YZcpC6XxmGabePPLyHCKbw/YXRugv3MRHH7MxE2hVHtryXeSYYnxcESb/3OUIQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.5.3': - resolution: {integrity: sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==} + '@next/swc-linux-arm64-gnu@16.0.5': + resolution: {integrity: sha512-meSLB52fw4tgDpPnyuhwA280EWLwwIntrxLYjzKU3e3730ur2WJAmmqoZ1LPIZ2l3eDfh9SBHnJGTczbgPeNeA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.5.3': - resolution: {integrity: sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==} + '@next/swc-linux-arm64-musl@16.0.5': + resolution: {integrity: sha512-aAJtQkvUzz5t0xVAmK931SIhWnSQAaEoTyG/sKPCYq2u835K/E4a14A+WRPd4dkhxIHNudE8dI+FpHekgdrA4g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.5.3': - resolution: {integrity: sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==} + '@next/swc-linux-x64-gnu@16.0.5': + resolution: {integrity: sha512-bYwbjBwooMWRhy6vRxenaYdguTM2hlxFt1QBnUF235zTnU2DhGpETm5WU93UvtAy0uhC5Kgqsl8RyNXlprFJ6Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.5.3': - resolution: {integrity: sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==} + '@next/swc-linux-x64-musl@16.0.5': + resolution: {integrity: sha512-iGv2K/4gW3mkzh+VcZTf2gEGX5o9xdb5oPqHjgZvHdVzCw0iSAJ7n9vKzl3SIEIIHZmqRsgNasgoLd0cxaD+tg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.5.3': - resolution: {integrity: sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==} + '@next/swc-win32-arm64-msvc@16.0.5': + resolution: {integrity: sha512-6xf52Hp4SH9+4jbYmfUleqkuxvdB9JJRwwFlVG38UDuEGPqpIA+0KiJEU9lxvb0RGNo2i2ZUhc5LHajij9H9+A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.5.3': - resolution: {integrity: sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==} + '@next/swc-win32-x64-msvc@16.0.5': + resolution: {integrity: sha512-06kTaOh+Qy/kguN+MMK+/VtKmRkQJrPlGQMvCUbABk1UxI5SKTgJhbmMj9Hf0qWwrS6g9JM6/Zk+etqeMyvHAw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -6101,13 +6150,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -6419,6 +6461,10 @@ packages: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} @@ -7424,9 +7470,6 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-bigint@1.1.0: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} @@ -8388,9 +8431,9 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - next@15.5.3: - resolution: {integrity: sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + next@16.0.5: + resolution: {integrity: sha512-XUPsFqSqu/NDdPfn/cju9yfIedkDI7ytDoALD9todaSMxk1Z5e3WcbUjfI9xsanFTys7xz62lnRWNFqJordzkQ==} + engines: {node: '>=20.9.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -9400,6 +9443,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} @@ -9454,8 +9502,8 @@ packages: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} - sharp@0.34.3: - resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -9544,9 +9592,6 @@ packages: resolution: {integrity: sha512-ZpzWAFHIFqyFE56dXqgX/DkDRZdz+rRcjoIk/RQU4IX0wiCv1l8S7ZrXDHcCc+uaf+6o7w3h2l3g6GYG5TKN9Q==} engines: {node: ^18.17.0 || >=20.5.0} - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -12691,90 +12736,101 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@img/sharp-darwin-arm64@0.34.3': + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - '@img/sharp-darwin-x64@0.34.3': + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.0': + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.0': + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.0': + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm@1.2.0': + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.0': + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.0': + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.0': + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.0': + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - '@img/sharp-linux-arm64@0.34.3': + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - '@img/sharp-linux-arm@0.34.3': + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - '@img/sharp-linux-ppc64@0.34.3': + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - '@img/sharp-linux-s390x@0.34.3': + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - '@img/sharp-linux-x64@0.34.3': + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.3': + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.3': + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - '@img/sharp-wasm32@0.34.3': + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.7.1 optional: true - '@img/sharp-win32-arm64@0.34.3': + '@img/sharp-win32-arm64@0.34.5': optional: true - '@img/sharp-win32-ia32@0.34.3': + '@img/sharp-win32-ia32@0.34.5': optional: true - '@img/sharp-win32-x64@0.34.3': + '@img/sharp-win32-x64@0.34.5': optional: true '@inquirer/checkbox@4.2.2(@types/node@24.1.0)': @@ -13316,30 +13372,30 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@15.5.3': {} + '@next/env@16.0.5': {} - '@next/swc-darwin-arm64@15.5.3': + '@next/swc-darwin-arm64@16.0.5': optional: true - '@next/swc-darwin-x64@15.5.3': + '@next/swc-darwin-x64@16.0.5': optional: true - '@next/swc-linux-arm64-gnu@15.5.3': + '@next/swc-linux-arm64-gnu@16.0.5': optional: true - '@next/swc-linux-arm64-musl@15.5.3': + '@next/swc-linux-arm64-musl@16.0.5': optional: true - '@next/swc-linux-x64-gnu@15.5.3': + '@next/swc-linux-x64-gnu@16.0.5': optional: true - '@next/swc-linux-x64-musl@15.5.3': + '@next/swc-linux-x64-musl@16.0.5': optional: true - '@next/swc-win32-arm64-msvc@15.5.3': + '@next/swc-win32-arm64-msvc@16.0.5': optional: true - '@next/swc-win32-x64-msvc@15.5.3': + '@next/swc-win32-x64-msvc@16.0.5': optional: true '@ngtools/webpack@20.3.6(@angular/compiler-cli@20.3.6(@angular/compiler@20.3.6)(typescript@5.8.2))(typescript@5.8.2)(webpack@5.101.2(@swc/core@1.13.5)(esbuild@0.25.9))': @@ -15996,18 +16052,6 @@ snapshots: color-name@1.1.4: {} - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - optional: true - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - optional: true - colorette@2.0.20: {} combined-stream@1.0.8: @@ -16293,6 +16337,9 @@ snapshots: detect-libc@2.0.4: optional: true + detect-libc@2.1.2: + optional: true + detect-node-es@1.1.0: {} detect-node@2.1.0: {} @@ -17528,9 +17575,6 @@ snapshots: is-arrayish@0.2.1: {} - is-arrayish@0.3.2: - optional: true - is-bigint@1.1.0: dependencies: has-bigints: 1.1.0 @@ -18665,9 +18709,9 @@ snapshots: neo-async@2.6.2: {} - next@15.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.90.0): + next@16.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.90.0): dependencies: - '@next/env': 15.5.3 + '@next/env': 16.0.5 '@swc/helpers': 0.5.15 caniuse-lite: 1.0.30001739 postcss: 8.4.31 @@ -18675,16 +18719,16 @@ snapshots: react-dom: 19.1.0(react@19.1.0) styled-jsx: 5.1.6(react@19.1.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.5.3 - '@next/swc-darwin-x64': 15.5.3 - '@next/swc-linux-arm64-gnu': 15.5.3 - '@next/swc-linux-arm64-musl': 15.5.3 - '@next/swc-linux-x64-gnu': 15.5.3 - '@next/swc-linux-x64-musl': 15.5.3 - '@next/swc-win32-arm64-msvc': 15.5.3 - '@next/swc-win32-x64-msvc': 15.5.3 + '@next/swc-darwin-arm64': 16.0.5 + '@next/swc-darwin-x64': 16.0.5 + '@next/swc-linux-arm64-gnu': 16.0.5 + '@next/swc-linux-arm64-musl': 16.0.5 + '@next/swc-linux-x64-gnu': 16.0.5 + '@next/swc-linux-x64-musl': 16.0.5 + '@next/swc-win32-arm64-msvc': 16.0.5 + '@next/swc-win32-x64-msvc': 16.0.5 sass: 1.90.0 - sharp: 0.34.3 + sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -19865,6 +19909,9 @@ snapshots: semver@7.7.2: {} + semver@7.7.3: + optional: true + send@0.19.0: dependencies: debug: 2.6.9 @@ -19965,34 +20012,36 @@ snapshots: dependencies: kind-of: 6.0.3 - sharp@0.34.3: + sharp@0.34.5: dependencies: - color: 4.2.3 - detect-libc: 2.0.4 - semver: 7.7.2 + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.3 - '@img/sharp-darwin-x64': 0.34.3 - '@img/sharp-libvips-darwin-arm64': 1.2.0 - '@img/sharp-libvips-darwin-x64': 1.2.0 - '@img/sharp-libvips-linux-arm': 1.2.0 - '@img/sharp-libvips-linux-arm64': 1.2.0 - '@img/sharp-libvips-linux-ppc64': 1.2.0 - '@img/sharp-libvips-linux-s390x': 1.2.0 - '@img/sharp-libvips-linux-x64': 1.2.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 - '@img/sharp-linux-arm': 0.34.3 - '@img/sharp-linux-arm64': 0.34.3 - '@img/sharp-linux-ppc64': 0.34.3 - '@img/sharp-linux-s390x': 0.34.3 - '@img/sharp-linux-x64': 0.34.3 - '@img/sharp-linuxmusl-arm64': 0.34.3 - '@img/sharp-linuxmusl-x64': 0.34.3 - '@img/sharp-wasm32': 0.34.3 - '@img/sharp-win32-arm64': 0.34.3 - '@img/sharp-win32-ia32': 0.34.3 - '@img/sharp-win32-x64': 0.34.3 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true shebang-command@2.0.0: @@ -20083,11 +20132,6 @@ snapshots: transitivePeerDependencies: - supports-color - simple-swizzle@0.2.2: - dependencies: - is-arrayish: 0.3.2 - optional: true - slash@3.0.0: {} slice-ansi@5.0.0: