From edf45be781c327566aa7a1c15c35ab5c679bb8c2 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Wed, 1 Oct 2025 17:51:03 +0200
Subject: [PATCH 001/145] wip
---
packages/core/src/files.ts | 12 +-
packages/core/src/index.ts | 2 +
packages/core/src/precognition.ts | 115 ++++++++++++++++++
packages/vue3/src/form.ts | 49 +++++++-
.../Pages/FormComponent/Precognition.vue | 34 ++++++
.../app/Http/Requests/StoreUserRequest.php | 21 ++++
playgrounds/vue3/package.json | 8 +-
playgrounds/vue3/resources/js/Pages/Home.vue | 25 ++++
playgrounds/vue3/routes/web.php | 6 +
tests/app/server.js | 46 +++++++
10 files changed, 311 insertions(+), 7 deletions(-)
create mode 100644 packages/core/src/precognition.ts
create mode 100644 packages/vue3/test-app/Pages/FormComponent/Precognition.vue
create mode 100644 playgrounds/vue3/app/Http/Requests/StoreUserRequest.php
diff --git a/packages/core/src/files.ts b/packages/core/src/files.ts
index 4d927f7b2..2817b3c7a 100644
--- a/packages/core/src/files.ts
+++ b/packages/core/src/files.ts
@@ -1,10 +1,16 @@
import { FormDataConvertible, RequestPayload } from './types'
+export function isFile(value: unknown): boolean {
+ return (
+ (typeof File !== 'undefined' && value instanceof File) ||
+ value instanceof Blob ||
+ (typeof FileList !== 'undefined' && value instanceof FileList && value.length > 0)
+ )
+}
+
export function hasFiles(data: RequestPayload | FormDataConvertible): boolean {
return (
- data instanceof File ||
- data instanceof Blob ||
- (data instanceof FileList && data.length > 0) ||
+ isFile(data) ||
(data instanceof FormData && Array.from(data.values()).some((value) => hasFiles(value))) ||
(typeof data === 'object' && data !== null && Object.values(data).some((value) => hasFiles(value)))
)
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 08ecfbaae..342bbb1a2 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,11 +1,13 @@
import { Router } from './router'
export { getScrollableParent } from './domUtils'
+export { hasFiles } from './files'
export { objectToFormData } from './formData'
export { formDataToObject } from './formObject'
export { default as createHeadManager } from './head'
export { default as useInfiniteScroll } from './infiniteScroll'
export { shouldIntercept, shouldNavigate } from './navigationEvents'
+export { default as usePrecognition } from './precognition'
export { hide as hideProgress, progress, reveal as revealProgress, default as setupProgress } from './progress'
export { resetFormFields } from './resetFormFields'
export * from './types'
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
new file mode 100644
index 000000000..044ee35cf
--- /dev/null
+++ b/packages/core/src/precognition.ts
@@ -0,0 +1,115 @@
+import { default as axios } from 'axios'
+import { get, isEqual } from 'lodash-es'
+import debounce from './debounce'
+import { hasFiles, isFile } from './files'
+import { ErrorBag, Errors, Method } from './types'
+
+type ValidatableData = Record
+
+export function forgetFiles(data: ValidatableData): ValidatableData {
+ const newData = { ...data }
+
+ Object.keys(newData).forEach((name) => {
+ const value = newData[name]
+
+ if (value === null) {
+ return
+ }
+
+ if (isFile(value)) {
+ delete newData[name]
+
+ return
+ }
+
+ if (Array.isArray(value)) {
+ newData[name] = Object.values(forgetFiles({ ...value }))
+
+ return
+ }
+
+ if (typeof value === 'object') {
+ newData[name] = forgetFiles(newData[name] as ValidatableData)
+
+ return
+ }
+ })
+
+ return newData
+}
+
+interface UsePrecognitionOptions {
+ onStart: () => void
+ onFinish: () => void
+ onPrecognitionSuccess: () => void
+ onValidationError: (errors: Errors & ErrorBag) => void
+}
+
+interface PrecognitionValidator {}
+
+export default function usePrecognition(precognitionOptions: UsePrecognitionOptions): PrecognitionValidator {
+ let oldData: ValidatableData = {}
+ let validatingData: ValidatableData = {}
+
+ let validateFiles: boolean = false
+ let debounceTimeoutDuration = 1500
+
+ const setDebounceTimeout = (value: number) => {
+ debounceTimeoutDuration = value
+ validate = createValidateFunction()
+ }
+
+ const createValidateFunction = () =>
+ debounce((options: { action: string; method: Method; data: ValidatableData; only: string[] }) => {
+ const data = validateFiles ? options.data : forgetFiles(options.data)
+
+ if (options.only && isEqual(get(data, options.only), get(oldData, options.only))) {
+ return
+ }
+
+ oldData = validatingData = { ...data }
+
+ precognitionOptions.onStart()
+
+ axios({
+ method: options.method,
+ url: options.action,
+ data: validatingData,
+ headers: {
+ 'Content-Type': hasFiles(data) ? 'multipart/form-data' : 'application/json',
+ Precognition: true,
+ ...(options.only.length ? { 'Precognition-Validate-Only': options.only.join(',') } : {}),
+ },
+ })
+ .then((response) => {
+ if (response.status === 204 && response.headers['precognition-success'] === 'true') {
+ return precognitionOptions.onPrecognitionSuccess()
+ }
+ })
+ .catch((error) => {
+ if (error.response && error.response.status === 422) {
+ return precognitionOptions.onValidationError(error.response.data.errors || {})
+ } else {
+ throw error
+ }
+ })
+ .finally(() => {
+ precognitionOptions.onFinish()
+ })
+ }, debounceTimeoutDuration)
+
+ let validate = createValidateFunction()
+
+ return {
+ setOldData(data: ValidatableData) {
+ oldData = { ...data }
+ },
+
+ validateFiles(value: boolean) {
+ validateFiles = value
+ },
+
+ validate,
+ setTimeout: setDebounceTimeout,
+ }
+}
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 630cbe6ff..f7be16b6d 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -8,6 +8,7 @@ import {
mergeDataIntoQueryString,
Method,
resetFormFields,
+ usePrecognition,
VisitOptions,
} from '@inertiajs/core'
import { isEqual } from 'lodash-es'
@@ -146,14 +147,16 @@ const Form: InertiaForm = defineComponent({
// expects an object, and submitting a FormData instance directly causes problems with nested objects.
const getData = (): Record => formDataToObject(getFormData())
- const submit = () => {
- const [action, data] = mergeDataIntoQueryString(
+ const getActionAndData = (): [string, Record] => {
+ return mergeDataIntoQueryString(
method.value,
isUrlMethodPair(props.action) ? props.action.url : props.action,
getData(),
props.queryStringArrayFormat,
)
+ }
+ const submit = () => {
const maybeReset = (resetOption: boolean | string[]) => {
if (!resetOption) {
return
@@ -193,12 +196,15 @@ const Form: InertiaForm = defineComponent({
...props.options,
}
+ const [action, data] = getActionAndData()
+
// We need transform because we can't override the default data with different keys (by design)
form.transform(() => props.transform(data)).submit(method.value, action, submitOptions)
}
const reset = (...fields: string[]) => {
resetFormFields(formElement.value, defaultData.value, fields)
+ validator.setOldData(getData()) // TODO: should it really do this?
}
const resetAndClearErrors = (...fields: string[]) => {
@@ -211,6 +217,37 @@ const Form: InertiaForm = defineComponent({
isDirty.value = false
}
+ const validating = ref(false)
+
+ const validator = usePrecognition({
+ onStart: () => {
+ validating.value = true
+ },
+ onFinish: () => {
+ validating.value = false
+ },
+ onPrecognitionSuccess: () => form.clearErrors(),
+ onValidationError: (errors) => form.setError(errors),
+ })
+
+ onMounted(() => {
+ const [_action, data] = getActionAndData()
+
+ // Set the initial data on the validator
+ validator.setOldData(data)
+ })
+
+ const validate = (field?: string | string[]) => {
+ const [action, data] = getActionAndData()
+
+ validator.validate({
+ action,
+ method: method.value,
+ data,
+ only: Array.isArray(field) ? field : [field],
+ })
+ }
+
const exposed = {
get errors() {
return form.errors
@@ -230,6 +267,9 @@ const Form: InertiaForm = defineComponent({
get recentlySuccessful() {
return form.recentlySuccessful
},
+ get validating() {
+ return validating.value
+ },
clearErrors: (...fields: string[]) => form.clearErrors(...fields),
resetAndClearErrors,
setError: (fieldOrFields: string | Record, maybeValue?: string) =>
@@ -240,6 +280,11 @@ const Form: InertiaForm = defineComponent({
reset,
submit,
defaults,
+
+ // Precognition
+ valid: (field: string) => form.errors[field] === undefined,
+ invalid: (field: string) => form.errors[field] !== undefined,
+ validate,
}
expose(exposed)
diff --git a/packages/vue3/test-app/Pages/FormComponent/Precognition.vue b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
new file mode 100644
index 000000000..9177235d5
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
Form Precognition
+
+
+
+
diff --git a/playgrounds/vue3/app/Http/Requests/StoreUserRequest.php b/playgrounds/vue3/app/Http/Requests/StoreUserRequest.php
new file mode 100644
index 000000000..392fa0763
--- /dev/null
+++ b/playgrounds/vue3/app/Http/Requests/StoreUserRequest.php
@@ -0,0 +1,21 @@
+|string>
+ */
+ public function rules(): array
+ {
+ return [
+ 'name' => ['required', 'string', 'min:3', 'max:255'],
+ 'email' => ['required', 'string', 'email', 'max:255'],
+ ];
+ }
+}
diff --git a/playgrounds/vue3/package.json b/playgrounds/vue3/package.json
index e089cc932..359dc0839 100644
--- a/playgrounds/vue3/package.json
+++ b/playgrounds/vue3/package.json
@@ -23,5 +23,9 @@
"vue": "^3.5.21",
"vue-tsc": "^2.2.12"
},
- "type": "module"
-}
+ "type": "module",
+ "dependencies": {
+ "laravel-precognition": "link:../../../precognition/packages/core",
+ "laravel-precognition-vue": "link:../../../precognition/packages/vue"
+ }
+}
\ No newline at end of file
diff --git a/playgrounds/vue3/resources/js/Pages/Home.vue b/playgrounds/vue3/resources/js/Pages/Home.vue
index be2f51b9e..3e0729551 100644
--- a/playgrounds/vue3/resources/js/Pages/Home.vue
+++ b/playgrounds/vue3/resources/js/Pages/Home.vue
@@ -5,6 +5,15 @@ export default { layout: Layout }
@@ -20,5 +29,21 @@ import { Head, Link, router } from '@inertiajs/vue3'
Clear History
+
+
diff --git a/playgrounds/vue3/routes/web.php b/playgrounds/vue3/routes/web.php
index efa24a69e..b51be2dba 100644
--- a/playgrounds/vue3/routes/web.php
+++ b/playgrounds/vue3/routes/web.php
@@ -1,6 +1,8 @@
middleware([HandlePrecognitiveRequests::class]);
+
Route::get('/users', function () {
sleep(2);
diff --git a/tests/app/server.js b/tests/app/server.js
index 13a56b838..92aba77df 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -884,6 +884,52 @@ app.get('/form-component/invalidate-tags/:propType', (req, res) =>
}),
)
+//
+
+app.post('/form-component/precognition', (req, res) => {
+ setTimeout(() => {
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const email = req.body['email']
+ const token = req.body['token']
+ const errors = {}
+
+ if (!name) {
+ errors.name = 'The name field is required.'
+ }
+
+ if (name && name.length < 3) {
+ errors.name = 'The name must be at least 3 characters.'
+ }
+
+ if (!email) {
+ errors.email = 'The email field is required.'
+ }
+
+ if (email && !/\S+@\S+\.\S+/.test(email)) {
+ errors.email = 'The email must be a valid email address.'
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
+
+ res.header('Precognition', 'true')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 500)
+})
+
+//
+
function renderInfiniteScroll(req, res, component, total = 40, orderByDesc = false, perPage = 15) {
const page = req.query.page ? parseInt(req.query.page) : 1
const partialReload = !!req.headers['x-inertia-partial-data']
From 3fd3af9487f83d284037b52567340ef86b80443b Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Wed, 1 Oct 2025 22:41:55 +0200
Subject: [PATCH 002/145] wip
---
packages/core/src/precognition.ts | 85 +++++++++++---------
packages/vue3/src/form.ts | 28 ++++---
playgrounds/vue3/resources/js/Pages/Home.vue | 9 +++
tests/app/server.js | 1 +
4 files changed, 73 insertions(+), 50 deletions(-)
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index 044ee35cf..9632dcad3 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -41,8 +41,6 @@ export function forgetFiles(data: ValidatableData): ValidatableData {
interface UsePrecognitionOptions {
onStart: () => void
onFinish: () => void
- onPrecognitionSuccess: () => void
- onValidationError: (errors: Errors & ErrorBag) => void
}
interface PrecognitionValidator {}
@@ -60,43 +58,54 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
}
const createValidateFunction = () =>
- debounce((options: { action: string; method: Method; data: ValidatableData; only: string[] }) => {
- const data = validateFiles ? options.data : forgetFiles(options.data)
-
- if (options.only && isEqual(get(data, options.only), get(oldData, options.only))) {
- return
- }
-
- oldData = validatingData = { ...data }
-
- precognitionOptions.onStart()
-
- axios({
- method: options.method,
- url: options.action,
- data: validatingData,
- headers: {
- 'Content-Type': hasFiles(data) ? 'multipart/form-data' : 'application/json',
- Precognition: true,
- ...(options.only.length ? { 'Precognition-Validate-Only': options.only.join(',') } : {}),
- },
- })
- .then((response) => {
- if (response.status === 204 && response.headers['precognition-success'] === 'true') {
- return precognitionOptions.onPrecognitionSuccess()
- }
+ debounce(
+ (options: {
+ url: string
+ method: Method
+ data: ValidatableData
+ only: string[]
+ errorBag?: string
+ onPrecognitionSuccess: () => void
+ onValidationError: (errors: Errors & ErrorBag) => void
+ }) => {
+ const data = validateFiles ? options.data : forgetFiles(options.data)
+
+ if (options.only && isEqual(get(data, options.only), get(oldData, options.only))) {
+ return
+ }
+
+ oldData = validatingData = { ...data }
+
+ precognitionOptions.onStart()
+
+ axios({
+ method: options.method,
+ url: options.url,
+ data: validatingData,
+ headers: {
+ 'Content-Type': hasFiles(data) ? 'multipart/form-data' : 'application/json',
+ Precognition: true,
+ ...(options.only.length ? { 'Precognition-Validate-Only': options.only.join(',') } : {}),
+ },
})
- .catch((error) => {
- if (error.response && error.response.status === 422) {
- return precognitionOptions.onValidationError(error.response.data.errors || {})
- } else {
- throw error
- }
- })
- .finally(() => {
- precognitionOptions.onFinish()
- })
- }, debounceTimeoutDuration)
+ .then((response) => {
+ if (response.status === 204 && response.headers['precognition-success'] === 'true') {
+ return options.onPrecognitionSuccess()
+ }
+ })
+ .catch((error) => {
+ if (error.response && error.response.status === 422) {
+ return options.onValidationError(error.response.data.errors || {})
+ } else {
+ throw error
+ }
+ })
+ .finally(() => {
+ precognitionOptions.onFinish()
+ })
+ },
+ debounceTimeoutDuration,
+ )
let validate = createValidateFunction()
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index f7be16b6d..b76e1fb83 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -220,14 +220,8 @@ const Form: InertiaForm = defineComponent({
const validating = ref(false)
const validator = usePrecognition({
- onStart: () => {
- validating.value = true
- },
- onFinish: () => {
- validating.value = false
- },
- onPrecognitionSuccess: () => form.clearErrors(),
- onValidationError: (errors) => form.setError(errors),
+ onStart: () => (validating.value = true),
+ onFinish: () => (validating.value = false),
})
onMounted(() => {
@@ -237,14 +231,24 @@ const Form: InertiaForm = defineComponent({
validator.setOldData(data)
})
- const validate = (field?: string | string[]) => {
- const [action, data] = getActionAndData()
+ const validate = (field: string | string[]) => {
+ const only = Array.isArray(field) ? field : [field]
+
+ const [url, data] = getActionAndData()
validator.validate({
- action,
+ url,
method: method.value,
data,
- only: Array.isArray(field) ? field : [field],
+ only,
+ onPrecognitionSuccess: () => form.clearErrors(...only),
+ onValidationError: (errors) => {
+ const scopedErrors = props.errorBag ? errors[props.errorBag || ''] || {} : errors
+ const errorKeys = Object.keys(scopedErrors)
+ const valid = only.filter((field) => !errorKeys.includes(field))
+ form.clearErrors(...valid)
+ form.setError(scopedErrors)
+ },
})
}
diff --git a/playgrounds/vue3/resources/js/Pages/Home.vue b/playgrounds/vue3/resources/js/Pages/Home.vue
index 3e0729551..1e0aaf3b5 100644
--- a/playgrounds/vue3/resources/js/Pages/Home.vue
+++ b/playgrounds/vue3/resources/js/Pages/Home.vue
@@ -14,6 +14,11 @@ const form = useForm('post', '/users', {
})
const submit = () => form.submit()
+
+const validate = () =>
+ form.validate({
+ only: ['name', 'email'],
+ })
@@ -30,6 +35,10 @@ const submit = () => form.submit()
+
+ PValidate
+
+
-
-
-
-
- {{ errors.token }}
-
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index a1aecddb8..81de9108e 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1431,6 +1431,69 @@ test.describe('Form Component', () => {
})
})
+ test.describe('Precognition', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/form-component/precognition')
+ })
+
+ test('shows validation error when field is invalid', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ })
+
+ test('clears validation error when field becomes valid', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ })
+
+ test('validates only the specified field', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).not.toBeVisible()
+ })
+
+ test('validates multiple fields independently', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
+
+ test('does not clear unrelated field errors', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+
+ await page.fill('input[name="email"]', 'test@example.com')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ })
+ })
+
test.describe('React', () => {
test.skip(process.env.PACKAGE !== 'react', 'Skipping React-specific tests')
From 41ce2a6696fa3c414283e258248001d9680306f1 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 2 Oct 2025 12:31:10 +0200
Subject: [PATCH 006/145] Update form-component.spec.ts
---
tests/form-component.spec.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 81de9108e..25bea80ad 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1432,6 +1432,8 @@ test.describe('Form Component', () => {
})
test.describe('Precognition', () => {
+ test.skip(process.env.PACKAGE !== 'vue', 'Precognition is Vue-only for now')
+
test.beforeEach(async ({ page }) => {
await page.goto('/form-component/precognition')
})
From 0a1d80ebab902f44eb7579ac6c12f0c38318fad2 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 2 Oct 2025 14:43:00 +0200
Subject: [PATCH 007/145] wip
---
packages/vue3/src/form.ts | 5 +++--
tests/form-component.spec.ts | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index e7330e511..19f250c0c 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -1,4 +1,5 @@
import {
+ Errors,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
@@ -248,7 +249,7 @@ const Form: InertiaForm = defineComponent({
},
onValidationError: (errors) => {
validated.value = [...validated.value, ...only]
- const scopedErrors = props.errorBag ? errors[props.errorBag || ''] || {} : errors
+ const scopedErrors = (props.errorBag ? errors[props.errorBag || ''] || {} : errors) as Errors
form.setError({ ...form.errors, ...scopedErrors })
},
})
@@ -288,7 +289,7 @@ const Form: InertiaForm = defineComponent({
defaults,
// Precognition
- valid: (field: string) => validated.value.has(field) && form.errors[field] === undefined,
+ valid: (field: string) => validated.value.includes(field) && form.errors[field] === undefined,
invalid: (field: string) => form.errors[field] !== undefined,
validate,
setValidationTimeout: (duration: number) => validator.setTimeout(duration),
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 25bea80ad..fbcfaacdf 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1432,7 +1432,7 @@ test.describe('Form Component', () => {
})
test.describe('Precognition', () => {
- test.skip(process.env.PACKAGE !== 'vue', 'Precognition is Vue-only for now')
+ test.skip(process.env.PACKAGE !== 'vue3', 'Precognition is Vue-only for now')
test.beforeEach(async ({ page }) => {
await page.goto('/form-component/precognition')
From b3608acfd55cbf8a5007b03122e1723458ee9d91 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 2 Oct 2025 14:50:33 +0200
Subject: [PATCH 008/145] Update package.json
---
playgrounds/vue3/package.json | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/playgrounds/vue3/package.json b/playgrounds/vue3/package.json
index 359dc0839..d36c26e42 100644
--- a/playgrounds/vue3/package.json
+++ b/playgrounds/vue3/package.json
@@ -23,9 +23,5 @@
"vue": "^3.5.21",
"vue-tsc": "^2.2.12"
},
- "type": "module",
- "dependencies": {
- "laravel-precognition": "link:../../../precognition/packages/core",
- "laravel-precognition-vue": "link:../../../precognition/packages/vue"
- }
+ "type": "module"
}
\ No newline at end of file
From dc31fa0af2cc3c1a28f68afc83bd12efa8338d9e Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 09:39:00 +0200
Subject: [PATCH 009/145] Update form.ts
---
packages/vue3/src/form.ts | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 19f250c0c..87e954886 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -137,6 +137,7 @@ const Form: InertiaForm = defineComponent({
onMounted(() => {
defaultData.value = getFormData()
formEvents.forEach((e) => formElement.value.addEventListener(e, onFormUpdate))
+ updateDataOnValidator()
})
onBeforeUnmount(() => formEvents.forEach((e) => formElement.value?.removeEventListener(e, onFormUpdate)))
@@ -203,9 +204,18 @@ const Form: InertiaForm = defineComponent({
form.transform(() => props.transform(data)).submit(method.value, action, submitOptions)
}
+ const updateDataOnValidator = () => {
+ try {
+ // This might fail if the component is already unmounted but this function
+ // is called after navigating away after a form submission.
+ const data = getData()
+ validator.setOldData(data)
+ } catch {}
+ }
+
const reset = (...fields: string[]) => {
resetFormFields(formElement.value, defaultData.value, fields)
- validator.setOldData(getData())
+ updateDataOnValidator()
}
const resetAndClearErrors = (...fields: string[]) => {
@@ -226,22 +236,18 @@ const Form: InertiaForm = defineComponent({
onFinish: () => (validating.value = false),
})
- onMounted(() => {
- const [_action, data] = getActionAndData()
-
- // Set the initial data on the validator
- validator.setOldData(data)
- })
-
const validate = (field: string | string[]) => {
const only = Array.isArray(field) ? field : [field]
- const [url, data] = getActionAndData()
+ // We're not using the data object from this method as it might be empty
+ // on GET requests, and we still want to pass a data object to the
+ // validator so it knows the current state of the form.
+ const [url] = getActionAndData()
validator.validate({
url,
method: method.value,
- data,
+ data: getData(),
only,
onPrecognitionSuccess: () => {
validated.value = [...validated.value, ...only]
From 6bf015a7046b5dc072f21bd4ab11e292dd561556 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 10:24:04 +0200
Subject: [PATCH 010/145] refactor
---
packages/core/src/files.ts | 32 ++++++++++++++++++++
packages/core/src/precognition.ts | 50 +++++--------------------------
packages/core/src/types.ts | 3 +-
3 files changed, 42 insertions(+), 43 deletions(-)
diff --git a/packages/core/src/files.ts b/packages/core/src/files.ts
index 2817b3c7a..34d271831 100644
--- a/packages/core/src/files.ts
+++ b/packages/core/src/files.ts
@@ -15,3 +15,35 @@ export function hasFiles(data: RequestPayload | FormDataConvertible): boolean {
(typeof data === 'object' && data !== null && Object.values(data).some((value) => hasFiles(value)))
)
}
+
+export function forgetFiles(data: Record): Record {
+ const newData = { ...data }
+
+ Object.keys(newData).forEach((name) => {
+ const value = newData[name]
+
+ if (value === null) {
+ return
+ }
+
+ if (isFile(value)) {
+ delete newData[name]
+
+ return
+ }
+
+ if (Array.isArray(value)) {
+ newData[name] = Object.values(forgetFiles({ ...value }))
+
+ return
+ }
+
+ if (typeof value === 'object') {
+ newData[name] = forgetFiles(newData[name] as Record)
+
+ return
+ }
+ })
+
+ return newData
+}
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index c401b5421..26cdf322e 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -1,42 +1,8 @@
import { default as axios } from 'axios'
import { get, isEqual } from 'lodash-es'
import debounce from './debounce'
-import { hasFiles, isFile } from './files'
-import { ErrorBag, Errors, Method } from './types'
-
-type ValidatableData = Record
-
-export function forgetFiles(data: ValidatableData): ValidatableData {
- const newData = { ...data }
-
- Object.keys(newData).forEach((name) => {
- const value = newData[name]
-
- if (value === null) {
- return
- }
-
- if (isFile(value)) {
- delete newData[name]
-
- return
- }
-
- if (Array.isArray(value)) {
- newData[name] = Object.values(forgetFiles({ ...value }))
-
- return
- }
-
- if (typeof value === 'object') {
- newData[name] = forgetFiles(newData[name] as ValidatableData)
-
- return
- }
- })
-
- return newData
-}
+import { forgetFiles, hasFiles } from './files'
+import { ErrorBag, Errors, Method, RequestData } from './types'
interface UsePrecognitionOptions {
onStart: () => void
@@ -46,7 +12,7 @@ interface UsePrecognitionOptions {
interface PrecognitionValidateOptions {
url: string
method: Method
- data: ValidatableData
+ data: RequestData
only: string[]
errorBag?: string
onPrecognitionSuccess: () => void
@@ -54,15 +20,15 @@ interface PrecognitionValidateOptions {
}
interface PrecognitionValidator {
- setOldData: (data: ValidatableData) => void
+ setOldData: (data: RequestData) => void
validateFiles: (value: boolean) => void
validate: (options: PrecognitionValidateOptions) => void
setTimeout: (value: number) => void
}
export default function usePrecognition(precognitionOptions: UsePrecognitionOptions): PrecognitionValidator {
- let oldData: ValidatableData = {}
- let validatingData: ValidatableData = {}
+ let oldData: RequestData = {}
+ let validatingData: RequestData = {}
let validateFiles: boolean = false
let debounceTimeoutDuration = 1500
@@ -75,7 +41,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
const createValidateFunction = () =>
debounce(
(options: PrecognitionValidateOptions) => {
- const data = validateFiles ? options.data : forgetFiles(options.data)
+ const data = validateFiles ? options.data : (forgetFiles(options.data) as RequestData)
const changed = options.only.filter((field) => !isEqual(get(data, field), get(oldData, field)))
if (options.only.length > 0 && changed.length === 0) {
@@ -120,7 +86,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
let validate = createValidateFunction()
return {
- setOldData(data: ValidatableData) {
+ setOldData(data: RequestData) {
oldData = { ...data }
},
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 1212a82a5..49d09b3f0 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -99,7 +99,8 @@ export type FormDataError = Partial, ErrorValue>>
export type Method = 'get' | 'post' | 'put' | 'patch' | 'delete'
-export type RequestPayload = Record | FormData
+export type RequestData = Record
+export type RequestPayload = RequestData | FormData
export interface PageProps {
[key: string]: unknown
From 3ef1f6d2744c890f3ebc98743275ddc32e1f2f94 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 11:05:41 +0200
Subject: [PATCH 011/145] wip
---
packages/core/src/types.ts | 8 ++++++++
packages/vue3/src/form.ts | 24 ++++++++++++++++++++----
2 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 49d09b3f0..bd64a891f 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -489,6 +489,14 @@ export type FormComponentMethods = {
defaults: () => void
}
+export type FormComponentPrecognition = {
+ valid: (field: string) => boolean
+ invalid: (field: string) => boolean
+ validate: (field?: string | string[]) => void
+ touch: (field: string | string[]) => void
+ validating: boolean
+}
+
export type FormComponentonSubmitCompleteArguments = Pick
export type FormComponentState = {
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 87e954886..994fae646 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -1,5 +1,6 @@
import {
Errors,
+ FormComponentPrecognition,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
@@ -216,6 +217,12 @@ const Form: InertiaForm = defineComponent({
const reset = (...fields: string[]) => {
resetFormFields(formElement.value, defaultData.value, fields)
updateDataOnValidator()
+
+ if (fields.length === 0) {
+ touched.value = []
+ } else {
+ touched.value = touched.value.filter((field) => !fields.includes(field))
+ }
}
const resetAndClearErrors = (...fields: string[]) => {
@@ -230,14 +237,15 @@ const Form: InertiaForm = defineComponent({
const validating = ref(false)
const validated = ref([])
+ const touched = ref([])
const validator = usePrecognition({
onStart: () => (validating.value = true),
onFinish: () => (validating.value = false),
})
- const validate = (field: string | string[]) => {
- const only = Array.isArray(field) ? field : [field]
+ const validate = (field?: string | string[]) => {
+ const only = field === undefined ? touched.value : Array.isArray(field) ? field : [field]
// We're not using the data object from this method as it might be empty
// on GET requests, and we still want to pass a data object to the
@@ -261,6 +269,13 @@ const Form: InertiaForm = defineComponent({
})
}
+ const touch = (field: string | string[]) => {
+ const fields = Array.isArray(field) ? field : [field]
+
+ // Use Set to avoid duplicates
+ touched.value = [...new Set([...touched.value, ...fields])]
+ }
+
const exposed = {
get errors() {
return form.errors
@@ -298,11 +313,12 @@ const Form: InertiaForm = defineComponent({
valid: (field: string) => validated.value.includes(field) && form.errors[field] === undefined,
invalid: (field: string) => form.errors[field] !== undefined,
validate,
+ touch,
setValidationTimeout: (duration: number) => validator.setTimeout(duration),
validateFiles: () => validator.validateFiles(true),
}
- expose(exposed)
+ expose(exposed)
return () => {
return h(
@@ -318,7 +334,7 @@ const Form: InertiaForm = defineComponent({
},
inert: props.disableWhileProcessing && form.processing,
},
- slots.default ? slots.default(exposed) : [],
+ slots.default ? slots.default(exposed) : [],
)
}
},
From 131dfe7e5ecb19732d7a3c72f8a29fd8fd119865 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 11:37:22 +0200
Subject: [PATCH 012/145] test
---
.../Pages/FormComponent/PrecognitionTouch.vue | 33 +++++++++++++++
playgrounds/vue3/resources/js/Pages/Home.vue | 40 -------------------
tests/form-component.spec.ts | 18 +++++++++
3 files changed, 51 insertions(+), 40 deletions(-)
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
new file mode 100644
index 000000000..00e9e4c51
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
Form Precognition Touch
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
+
+
+
+
+ {{ errors.email }}
+
+
+
+ Validate All Touched
+
+
+
diff --git a/playgrounds/vue3/resources/js/Pages/Home.vue b/playgrounds/vue3/resources/js/Pages/Home.vue
index 52f22457d..be2f51b9e 100644
--- a/playgrounds/vue3/resources/js/Pages/Home.vue
+++ b/playgrounds/vue3/resources/js/Pages/Home.vue
@@ -5,24 +5,6 @@ export default { layout: Layout }
@@ -38,27 +20,5 @@ const validate = () =>
Clear History
-
-
- PValidate
-
-
-
- Validating...
-
- Name
-
-
- {{ form.errors.name }}
-
-
- Email
-
-
- {{ form.errors.email }}
-
-
- Create User
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index fbcfaacdf..bb2ae8daa 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1494,6 +1494,24 @@ test.describe('Form Component', () => {
await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
})
+
+ test('validates all touched fields when calling validate() without arguments', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
})
test.describe('React', () => {
From fb3f9e6fd1a831644922fcdcd93ea92a5c0f714b Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 12:13:44 +0200
Subject: [PATCH 013/145] tests
---
.../Pages/FormComponent/PrecognitionTouch.vue | 4 +-
tests/form-component.spec.ts | 51 +++++++++++++++++++
2 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
index 00e9e4c51..aded3eb0f 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
@@ -9,7 +9,7 @@ import { Form } from '@inertiajs/vue3'
Validating...
@@ -28,6 +28,8 @@ import { Form } from '@inertiajs/vue3'
Validate All Touched
+ Reset All
+ Reset Name
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index bb2ae8daa..5ef93a2b1 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1504,14 +1504,65 @@ test.describe('Form Component', () => {
await page.fill('input[name="email"]', 'x')
await page.locator('input[name="email"]').blur()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
await page.getByRole('button', { name: 'Validate All Touched' }).click()
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
})
+
+ test('reset all fields clears all touched fields', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await page.getByRole('button', { name: 'Reset All' }).click()
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ })
+
+ test('reset specific fields removes only those fields from touched', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.locator('input[name="name"]')).toHaveValue('ab')
+ await expect(page.locator('input[name="email"]')).toHaveValue('x')
+
+ await page.getByRole('button', { name: 'Reset Name' }).click()
+
+ await expect(page.locator('input[name="name"]')).toHaveValue('')
+ await expect(page.locator('input[name="email"]')).toHaveValue('x')
+
+ await page.fill('input[name="name"]', 'abc')
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
})
test.describe('React', () => {
From d205af11a4af4d1d463cbdc22c096ac88d9e449d Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 12:25:29 +0200
Subject: [PATCH 014/145] wip
---
.../Pages/FormComponent/PrecognitionTouch.vue | 5 +
tests/form-component.spec.ts | 110 +++++++++++++++++-
2 files changed, 114 insertions(+), 1 deletion(-)
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
index aded3eb0f..89ba2c43f 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
@@ -28,8 +28,13 @@ import { Form } from '@inertiajs/vue3'
Validate All Touched
+ Validate Name
+ Validate Name and Email
Reset All
Reset Name
+ Reset Name and Email
+ Touch Name and Email
+ Touch Name Twice
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 5ef93a2b1..699f4c0e4 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1548,7 +1548,7 @@ test.describe('Form Component', () => {
await expect(page.locator('input[name="name"]')).toHaveValue('ab')
await expect(page.locator('input[name="email"]')).toHaveValue('x')
- await page.getByRole('button', { name: 'Reset Name' }).click()
+ await page.getByRole('button', { name: 'Reset Name', exact: true }).click()
await expect(page.locator('input[name="name"]')).toHaveValue('')
await expect(page.locator('input[name="email"]')).toHaveValue('x')
@@ -1563,6 +1563,114 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
})
+
+ test('touch with array marks multiple fields as touched', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.fill('input[name="email"]', 'x')
+
+ await page.getByRole('button', { name: 'Touch Name and Email' }).click()
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
+
+ test('touch deduplicates fields when called multiple times', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+
+ await page.getByRole('button', { name: 'Touch Name Twice' }).click()
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ })
+
+ test('validate with specific field works independently of touched state', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.fill('input[name="email"]', 'x')
+
+ await page.getByRole('button', { name: 'Validate Name', exact: true }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ })
+
+ test('validate with array of fields validates multiple fields', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.fill('input[name="email"]', 'x')
+
+ await page.getByRole('button', { name: 'Validate Name and Email' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
+
+ test('reset with array removes multiple fields from touched', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+
+ await page.getByRole('button', { name: 'Reset Name and Email' }).click()
+
+ await page.fill('input[name="name"]', 'abc')
+ await page.fill('input[name="email"]', 'test@example.com')
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await page.waitForTimeout(500)
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ })
+
+ test('touching one field and validating another does not validate the touched field', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+
+ await page.getByRole('button', { name: 'Validate Name', exact: true }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ })
})
test.describe('React', () => {
From e73b45dacca8305acddc99ead5b78c2d535621bc Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 12:30:43 +0200
Subject: [PATCH 015/145] split
---
.../Pages/FormComponent/PrecognitionReset.vue | 36 +++++++++++++++++++
.../Pages/FormComponent/PrecognitionTouch.vue | 17 +++++----
.../FormComponent/PrecognitionValidate.vue | 34 ++++++++++++++++++
tests/form-component.spec.ts | 12 +++----
4 files changed, 86 insertions(+), 13 deletions(-)
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
new file mode 100644
index 000000000..2b20a56d6
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
Form Precognition Reset
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
+
+
+
+
+ {{ errors.email }}
+
+
+
+ Validate All Touched
+ Reset All
+ Reset Name
+ Reset Name and Email
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
index 89ba2c43f..363cc6e94 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
@@ -9,7 +9,7 @@ import { Form } from '@inertiajs/vue3'
Validating...
@@ -28,13 +28,16 @@ import { Form } from '@inertiajs/vue3'
Validate All Touched
- Validate Name
- Validate Name and Email
- Reset All
- Reset Name
- Reset Name and Email
Touch Name and Email
- Touch Name Twice
+
+ Touch Name Twice
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
new file mode 100644
index 000000000..7fb72fee2
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
Form Precognition Validate
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
+
+
+
+
+ {{ errors.email }}
+
+
+
+ Validate Name
+ Validate Name and Email
+
+
+
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 699f4c0e4..514ef17e8 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1518,7 +1518,7 @@ test.describe('Form Component', () => {
})
test('reset all fields clears all touched fields', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-reset')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1537,7 +1537,7 @@ test.describe('Form Component', () => {
})
test('reset specific fields removes only those fields from touched', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-reset')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1596,7 +1596,7 @@ test.describe('Form Component', () => {
})
test('validate with specific field works independently of touched state', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-validate')
await page.fill('input[name="name"]', 'ab')
await page.fill('input[name="email"]', 'x')
@@ -1611,7 +1611,7 @@ test.describe('Form Component', () => {
})
test('validate with array of fields validates multiple fields', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-validate')
await page.fill('input[name="name"]', 'ab')
await page.fill('input[name="email"]', 'x')
@@ -1626,7 +1626,7 @@ test.describe('Form Component', () => {
})
test('reset with array removes multiple fields from touched', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-reset')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1656,7 +1656,7 @@ test.describe('Form Component', () => {
})
test('touching one field and validating another does not validate the touched field', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-validate')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
From 3e43529bf5054693300c81aba7fef4abcad05a5c Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 13:17:33 +0200
Subject: [PATCH 016/145] valid method
---
packages/vue3/src/form.ts | 7 ++
.../Pages/FormComponent/Precognition.vue | 8 +-
.../Pages/FormComponent/PrecognitionTouch.vue | 6 +-
tests/form-component.spec.ts | 85 +++++++++++++++++++
4 files changed, 103 insertions(+), 3 deletions(-)
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 994fae646..43ab7abc0 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -263,6 +263,13 @@ const Form: InertiaForm = defineComponent({
},
onValidationError: (errors) => {
validated.value = [...validated.value, ...only]
+
+ const validFields = only.filter((field) => errors[field] === undefined)
+
+ if (validFields.length) {
+ form.clearErrors(...validFields)
+ }
+
const scopedErrors = (props.errorBag ? errors[props.errorBag || ''] || {} : errors) as Errors
form.setError({ ...form.errors, ...scopedErrors })
},
diff --git a/packages/vue3/test-app/Pages/FormComponent/Precognition.vue b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
index d470d15d9..8a3cc6a72 100644
--- a/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
@@ -6,7 +6,11 @@ import { Form } from '@inertiajs/vue3'
Form Precognition
-
+
Validating...
@@ -14,6 +18,7 @@ import { Form } from '@inertiajs/vue3'
{{ errors.name }}
+
Name is valid!
@@ -21,6 +26,7 @@ import { Form } from '@inertiajs/vue3'
{{ errors.email }}
+
Email is valid!
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
index 363cc6e94..80209562c 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
@@ -32,8 +32,10 @@ import { Form } from '@inertiajs/vue3'
{
+ touch('name')
+ touch('name')
+ }
"
>
Touch Name Twice
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 514ef17e8..da8e4648c 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1495,6 +1495,91 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
})
+ test('valid returns false before validation', async ({ page }) => {
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ })
+
+ test('valid returns false when field has validation errors', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+ })
+
+ test('valid returns true after successful validation', async ({ page }) => {
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ })
+
+ test('invalid returns true only when validation errors exist', async ({ page }) => {
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ })
+
+ test('valid persists after successful validation', async ({ page }) => {
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'Jane Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ })
+
+ test('valid becomes false when field is re-validated with errors', async ({ page }) => {
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+ })
+
test('validates all touched fields when calling validate() without arguments', async ({ page }) => {
await page.goto('/form-component/precognition-touch')
From 2c23e1d72f9fbdd07ae14cc7149031acc23f4c07 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 13:27:57 +0200
Subject: [PATCH 017/145] props
---
packages/core/src/precognition.ts | 6 ++-
packages/core/src/types.ts | 2 +
packages/vue3/src/form.ts | 40 ++++++++++++++-----
.../Pages/FormComponent/Precognition.vue | 1 +
.../Pages/FormComponent/PrecognitionReset.vue | 1 +
.../Pages/FormComponent/PrecognitionTouch.vue | 1 +
.../FormComponent/PrecognitionValidate.vue | 1 +
7 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index 26cdf322e..eb5f2bb5c 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -34,8 +34,10 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
let debounceTimeoutDuration = 1500
const setDebounceTimeout = (value: number) => {
- debounceTimeoutDuration = value
- validate = createValidateFunction()
+ if (value !== debounceTimeoutDuration) {
+ debounceTimeoutDuration = value
+ validate = createValidateFunction()
+ }
}
const createValidateFunction = () =>
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index bd64a891f..7f49c116c 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -477,6 +477,8 @@ export type FormComponentProps = Partial<
resetOnSuccess?: boolean | string[]
resetOnError?: boolean | string[]
setDefaultsOnSuccess?: boolean
+ validateFiles?: boolean
+ validateTimeout?: number
}
export type FormComponentMethods = {
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 43ab7abc0..139b12948 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -14,7 +14,7 @@ import {
VisitOptions,
} from '@inertiajs/core'
import { isEqual } from 'lodash-es'
-import { computed, defineComponent, DefineComponent, h, onBeforeUnmount, onMounted, PropType, ref } from 'vue'
+import { computed, defineComponent, DefineComponent, h, onBeforeUnmount, onMounted, PropType, ref, watch } from 'vue'
import useForm from './useForm'
type InertiaForm = DefineComponent
@@ -113,6 +113,14 @@ const Form: InertiaForm = defineComponent({
type: [String, Array] as PropType,
default: () => [],
},
+ validateFiles: {
+ type: Boolean as PropType,
+ default: false,
+ },
+ validateTimeout: {
+ type: Number as PropType,
+ default: 1500,
+ },
},
setup(props, { slots, attrs, expose }) {
const form = useForm>({})
@@ -135,12 +143,33 @@ const Form: InertiaForm = defineComponent({
const formEvents: Array = ['input', 'change', 'reset']
+ const validating = ref(false)
+ const validated = ref([])
+ const touched = ref([])
+
+ const validator = usePrecognition({
+ onStart: () => (validating.value = true),
+ onFinish: () => (validating.value = false),
+ })
+
onMounted(() => {
defaultData.value = getFormData()
formEvents.forEach((e) => formElement.value.addEventListener(e, onFormUpdate))
updateDataOnValidator()
})
+ watch(
+ () => props.validateFiles,
+ (value) => validator.validateFiles(value),
+ { immediate: true },
+ )
+
+ watch(
+ () => props.validateTimeout,
+ (value) => validator.setTimeout(value),
+ { immediate: true },
+ )
+
onBeforeUnmount(() => formEvents.forEach((e) => formElement.value?.removeEventListener(e, onFormUpdate)))
const getFormData = (): FormData => new FormData(formElement.value)
@@ -235,15 +264,6 @@ const Form: InertiaForm = defineComponent({
isDirty.value = false
}
- const validating = ref(false)
- const validated = ref([])
- const touched = ref([])
-
- const validator = usePrecognition({
- onStart: () => (validating.value = true),
- onFinish: () => (validating.value = false),
- })
-
const validate = (field?: string | string[]) => {
const only = field === undefined ? touched.value : Array.isArray(field) ? field : [field]
diff --git a/packages/vue3/test-app/Pages/FormComponent/Precognition.vue b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
index 8a3cc6a72..32aabe1a5 100644
--- a/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
@@ -9,6 +9,7 @@ import { Form } from '@inertiajs/vue3'
Validating...
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
index 2b20a56d6..3d225f5e8 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
@@ -9,6 +9,7 @@ import { Form } from '@inertiajs/vue3'
Validating...
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
index 80209562c..dbe20d6cf 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
@@ -9,6 +9,7 @@ import { Form } from '@inertiajs/vue3'
Validating...
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
index 7fb72fee2..0398592ee 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
@@ -9,6 +9,7 @@ import { Form } from '@inertiajs/vue3'
Validating...
From c2e9fd43ad0f335d5a183e9f1147c7fd05f925b2 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 14:11:07 +0200
Subject: [PATCH 018/145] files
---
packages/core/src/precognition.ts | 28 +++++------
.../Pages/FormComponent/PrecognitionFiles.vue | 44 ++++++++++++++++++
tests/app/server.js | 46 ++++++++++++++++++-
tests/form-component.spec.ts | 39 ++++++++++++++++
4 files changed, 143 insertions(+), 14 deletions(-)
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index eb5f2bb5c..9d04cf0c1 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -1,7 +1,8 @@
-import { default as axios } from 'axios'
+import { default as axios, AxiosRequestConfig } from 'axios'
import { get, isEqual } from 'lodash-es'
import debounce from './debounce'
import { forgetFiles, hasFiles } from './files'
+import { objectToFormData } from './formData'
import { ErrorBag, Errors, Method, RequestData } from './types'
interface UsePrecognitionOptions {
@@ -27,11 +28,10 @@ interface PrecognitionValidator {
}
export default function usePrecognition(precognitionOptions: UsePrecognitionOptions): PrecognitionValidator {
- let oldData: RequestData = {}
- let validatingData: RequestData = {}
-
- let validateFiles: boolean = false
let debounceTimeoutDuration = 1500
+ let validateFiles: boolean = false
+
+ let oldData: RequestData = {}
const setDebounceTimeout = (value: number) => {
if (value !== debounceTimeoutDuration) {
@@ -43,33 +43,35 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
const createValidateFunction = () =>
debounce(
(options: PrecognitionValidateOptions) => {
+ const changed = options.only.filter((field) => !isEqual(get(options.data, field), get(oldData, field)))
const data = validateFiles ? options.data : (forgetFiles(options.data) as RequestData)
- const changed = options.only.filter((field) => !isEqual(get(data, field), get(oldData, field)))
if (options.only.length > 0 && changed.length === 0) {
return
}
- validatingData = { ...data }
-
precognitionOptions.onStart()
- axios({
+ const submitOptions: AxiosRequestConfig = {
method: options.method,
url: options.url,
- data: validatingData,
+ data: hasFiles(data) ? objectToFormData(data) : { ...data },
headers: {
- 'Content-Type': hasFiles(data) ? 'multipart/form-data' : 'application/json',
+ 'X-Requested-With': 'XMLHttpRequest',
Precognition: true,
...(options.only.length ? { 'Precognition-Validate-Only': options.only.join(',') } : {}),
},
- })
+ }
+
+ axios(submitOptions)
.then((response) => {
+ console.log({ response })
if (response.status === 204 && response.headers['precognition-success'] === 'true') {
options.onPrecognitionSuccess()
}
})
.catch((error) => {
+ console.log({ error })
if (error.response?.status === 422) {
return options.onValidationError(error.response.data?.errors || {})
}
@@ -77,7 +79,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
throw error
})
.finally(() => {
- oldData = { ...validatingData }
+ oldData = { ...data }
precognitionOptions.onFinish()
})
},
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
new file mode 100644
index 000000000..271e118a2
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
Form Precognition Files
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
Name is valid!
+
+
+
+
+
+ {{ errors.avatar }}
+
+
Avatar is valid!
+
+
+
+ Toggle Validate Files ({{ validateFilesEnabled ? 'enabled' : 'disabled' }})
+
+
+ Validate Both
+
+
+
diff --git a/tests/app/server.js b/tests/app/server.js
index 30ab1b58f..fdf433854 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -891,7 +891,6 @@ app.post('/form-component/precognition', (req, res) => {
const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
const name = req.body['name']
const email = req.body['email']
- const token = req.body['token']
const errors = {}
if (!name) {
@@ -929,6 +928,51 @@ app.post('/form-component/precognition', (req, res) => {
}, 500)
})
+app.get('/form-component/precognition-files', (req, res) => {
+ inertia.render(req, res, {
+ component: 'FormComponent/PrecognitionFiles',
+ })
+})
+
+app.post('/form-component/precognition-files', upload.any(), (req, res) => {
+ setTimeout(() => {
+ console.log(req, req)
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const hasAvatar = req.files && req.files.avatar
+ const errors = {}
+
+ if (!name) {
+ errors.name = 'The name field is required.'
+ }
+
+ if (name && name.length < 3) {
+ errors.name = 'The name must be at least 3 characters.'
+ }
+
+ if (!hasAvatar) {
+ errors.avatar = 'The avatar field is required.'
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 500)
+})
+
//
function renderInfiniteScroll(req, res, component, total = 40, orderByDesc = false, perPage = 15) {
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index da8e4648c..6e818cd4f 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1756,6 +1756,45 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
})
+
+ test('does not submit files by default', async ({ page }) => {
+ await page.goto('/form-component/precognition-files')
+
+ await page.setInputFiles('#avatar', {
+ name: 'avatar.jpg',
+ mimeType: 'image/jpeg',
+ buffer: Buffer.from('fake image data'),
+ })
+
+ await page.getByRole('button', { name: 'Validate Both' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name field is required.')).toBeVisible()
+ await expect(page.getByText('The avatar field is required.')).toBeVisible()
+ })
+
+ test('validates files when validate-files prop is true', async ({ page }) => {
+ await page.goto('/form-component/precognition-files')
+ await page.getByRole('button', { name: /Toggle Validate Files/ }).click()
+ await expect(page.getByText('Toggle Validate Files (enabled)')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.setInputFiles('#avatar', {
+ name: 'avatar.jpg',
+ mimeType: 'image/jpeg',
+ buffer: Buffer.from('fake image data'),
+ })
+
+ await page.getByRole('button', { name: 'Validate Both' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The avatar field is required.')).not.toBeVisible()
+ })
})
test.describe('React', () => {
From a54f9a54dc576d55cb193c74ad94fa8972359cc7 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 14:11:28 +0200
Subject: [PATCH 019/145] Update precognition.ts
---
packages/core/src/precognition.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index 9d04cf0c1..d1bf0987e 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -65,13 +65,11 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
axios(submitOptions)
.then((response) => {
- console.log({ response })
if (response.status === 204 && response.headers['precognition-success'] === 'true') {
options.onPrecognitionSuccess()
}
})
.catch((error) => {
- console.log({ error })
if (error.response?.status === 422) {
return options.onValidationError(error.response.data?.errors || {})
}
From 85a1398da9e8475171ceab339c7e7060f6dc901a Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 14:19:57 +0200
Subject: [PATCH 020/145] react
---
packages/react/src/Form.ts | 113 +++++++++++++++++-
.../Pages/FormComponent/Precognition.tsx | 29 +++++
.../Pages/FormComponent/PrecognitionFiles.tsx | 45 +++++++
.../Pages/FormComponent/PrecognitionReset.tsx | 40 +++++++
.../Pages/FormComponent/PrecognitionTouch.tsx | 43 +++++++
.../FormComponent/PrecognitionValidate.tsx | 34 ++++++
tests/form-component.spec.ts | 2 -
7 files changed, 302 insertions(+), 4 deletions(-)
create mode 100644 packages/react/test-app/Pages/FormComponent/Precognition.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index fb4a117b7..7c311971d 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -1,4 +1,6 @@
import {
+ Errors,
+ FormComponentPrecognition,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
@@ -8,6 +10,7 @@ import {
mergeDataIntoQueryString,
Method,
resetFormFields,
+ usePrecognition,
VisitOptions,
} from '@inertiajs/core'
import { isEqual } from 'lodash-es'
@@ -32,7 +35,7 @@ const deferStateUpdate = (callback: () => void) => {
type ComponentProps = (FormComponentProps &
Omit, keyof FormComponentProps | 'children'> &
Omit, keyof FormComponentProps | 'children'>) & {
- children: ReactNode | ((props: FormComponentSlotProps) => ReactNode)
+ children: ReactNode | ((props: FormComponentSlotProps & FormComponentPrecognition) => ReactNode)
}
type FormSubmitOptions = Omit
@@ -64,6 +67,8 @@ const Form = forwardRef(
resetOnSuccess = false,
setDefaultsOnSuccess = false,
invalidateCacheTags = [],
+ validateFiles = false,
+ validateTimeout = 1500,
children,
...props
},
@@ -79,6 +84,19 @@ const Form = forwardRef(
const [isDirty, setIsDirty] = useState(false)
const defaultData = useRef(new FormData())
+ const [validating, setValidating] = useState(false)
+ const [validated, setValidated] = useState([])
+ const [touched, setTouched] = useState([])
+
+ const validator = useMemo(
+ () =>
+ usePrecognition({
+ onStart: () => setValidating(true),
+ onFinish: () => setValidating(false),
+ }),
+ [],
+ )
+
const getFormData = (): FormData => new FormData(formElement.current)
// Convert the FormData to an object because we can't compare two FormData
@@ -101,8 +119,36 @@ const Form = forwardRef(
return () => formEvents.forEach((e) => formElement.current?.removeEventListener(e, updateDirtyState))
}, [])
+ useEffect(() => {
+ validator.validateFiles(validateFiles)
+ }, [validateFiles, validator])
+
+ useEffect(() => {
+ validator.setTimeout(validateTimeout)
+ }, [validateTimeout, validator])
+
+ useEffect(() => {
+ updateDataOnValidator()
+ }, [])
+
+ const updateDataOnValidator = () => {
+ try {
+ // This might fail if the component is already unmounted but this function
+ // is called after navigating away after a form submission.
+ const data = getData()
+ validator.setOldData(data)
+ } catch {}
+ }
+
const reset = (...fields: string[]) => {
resetFormFields(formElement.current, defaultData.current, fields)
+ updateDataOnValidator()
+
+ if (fields.length === 0) {
+ setTouched([])
+ } else {
+ setTouched((prev) => prev.filter((field) => !fields.includes(field)))
+ }
}
const resetAndClearErrors = (...fields: string[]) => {
@@ -169,6 +215,50 @@ const Form = forwardRef(
setIsDirty(false)
}
+ const validate = (field?: string | string[]) => {
+ const only = field === undefined ? touched : Array.isArray(field) ? field : [field]
+
+ // We're not using the data object from this method as it might be empty
+ // on GET requests, and we still want to pass a data object to the
+ // validator so it knows the current state of the form.
+ const [url] = mergeDataIntoQueryString(
+ resolvedMethod,
+ isUrlMethodPair(action) ? action.url : action,
+ getData(),
+ queryStringArrayFormat,
+ )
+
+ validator.validate({
+ url,
+ method: resolvedMethod,
+ data: getData(),
+ only,
+ onPrecognitionSuccess: () => {
+ setValidated((prev) => [...prev, ...only])
+ form.clearErrors(...only)
+ },
+ onValidationError: (errors) => {
+ setValidated((prev) => [...prev, ...only])
+
+ const validFields = only.filter((field) => errors[field] === undefined)
+
+ if (validFields.length) {
+ form.clearErrors(...validFields)
+ }
+
+ const scopedErrors = (errorBag ? errors[errorBag || ''] || {} : errors) as Errors
+ form.setError({ ...form.errors, ...scopedErrors })
+ },
+ })
+ }
+
+ const touch = (field: string | string[]) => {
+ const fields = Array.isArray(field) ? field : [field]
+
+ // Use Set to avoid duplicates
+ setTouched((prev) => [...new Set([...prev, ...fields])])
+ }
+
const exposed = () => ({
errors: form.errors,
hasErrors: form.hasErrors,
@@ -183,9 +273,28 @@ const Form = forwardRef(
reset,
submit,
defaults,
+
+ // Precognition
+ validating,
+ valid: (field: string) => validated.includes(field) && form.errors[field] === undefined,
+ invalid: (field: string) => form.errors[field] !== undefined,
+ validate,
+ touch,
+ setValidationTimeout: (duration: number) => validator.setTimeout(duration),
+ validateFiles: () => validator.validateFiles(true),
})
- useImperativeHandle(ref, exposed, [form, isDirty, submit])
+ useImperativeHandle(ref, exposed, [
+ form,
+ isDirty,
+ submit,
+ validating,
+ validated,
+ touched,
+ validate,
+ touch,
+ validator,
+ ])
return createElement(
'form',
diff --git a/packages/react/test-app/Pages/FormComponent/Precognition.tsx b/packages/react/test-app/Pages/FormComponent/Precognition.tsx
new file mode 100644
index 000000000..845aecd63
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/Precognition.tsx
@@ -0,0 +1,29 @@
+import { Form } from '@inertiajs/react'
+
+export default () => {
+ return (
+
+
Form Precognition
+
+
+ {({ invalid, errors, validate, valid, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
validate('name')} />
+ {invalid('name') &&
{errors.name}
}
+ {valid('name') &&
Name is valid!
}
+
+
+
+
validate('email')} />
+ {invalid('email') &&
{errors.email}
}
+ {valid('email') &&
Email is valid!
}
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
new file mode 100644
index 000000000..db7db4b46
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
@@ -0,0 +1,45 @@
+import { Form } from '@inertiajs/react'
+import { useState } from 'react'
+
+export default () => {
+ const [validateFilesEnabled, setValidateFilesEnabled] = useState(false)
+
+ return (
+
+
Form Precognition Files
+
+
+ {({ invalid, errors, validate, valid, validating, touch }) => (
+ <>
+ {validating && Validating...
}
+
+
+
validate('name')} />
+ {invalid('name') &&
{errors.name}
}
+ {valid('name') &&
Name is valid!
}
+
+
+
+
+ {invalid('avatar') &&
{errors.avatar}
}
+ {valid('avatar') &&
Avatar is valid!
}
+
+
+ setValidateFilesEnabled(!validateFilesEnabled)}>
+ Toggle Validate Files ({validateFilesEnabled ? 'enabled' : 'disabled'})
+
+
+ validate(['name', 'avatar'])}>
+ Validate Both
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx
new file mode 100644
index 000000000..50a267fe3
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx
@@ -0,0 +1,40 @@
+import { Form } from '@inertiajs/react'
+
+export default () => {
+ return (
+
+
Form Precognition Reset
+
+
+ {({ invalid, errors, validate, touch, validating, reset }) => (
+ <>
+ {validating && Validating...
}
+
+
+
touch('name')} />
+ {invalid('name') &&
{errors.name}
}
+
+
+
+
touch('email')} />
+ {invalid('email') &&
{errors.email}
}
+
+
+ validate()}>
+ Validate All Touched
+
+ reset()}>
+ Reset All
+
+ reset('name')}>
+ Reset Name
+
+ reset('name', 'email')}>
+ Reset Name and Email
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
new file mode 100644
index 000000000..2122b7b0c
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
@@ -0,0 +1,43 @@
+import { Form } from '@inertiajs/react'
+
+export default () => {
+ return (
+
+
Form Precognition Touch
+
+
+ {({ invalid, errors, validate, touch, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
touch('name')} />
+ {invalid('name') &&
{errors.name}
}
+
+
+
+
touch('email')} />
+ {invalid('email') &&
{errors.email}
}
+
+
+ validate()}>
+ Validate All Touched
+
+ touch(['name', 'email'])}>
+ Touch Name and Email
+
+ {
+ touch('name')
+ touch('name')
+ }}
+ >
+ Touch Name Twice
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx
new file mode 100644
index 000000000..ae7c47070
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx
@@ -0,0 +1,34 @@
+import { Form } from '@inertiajs/react'
+
+export default () => {
+ return (
+
+
Form Precognition Validate
+
+
+ {({ invalid, errors, validate, touch, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
touch('name')} />
+ {invalid('name') &&
{errors.name}
}
+
+
+
+
+ {invalid('email') &&
{errors.email}
}
+
+
+ validate('name')}>
+ Validate Name
+
+ validate(['name', 'email'])}>
+ Validate Name and Email
+
+ >
+ )}
+
+
+ )
+}
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 6e818cd4f..7a9d9c214 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1432,8 +1432,6 @@ test.describe('Form Component', () => {
})
test.describe('Precognition', () => {
- test.skip(process.env.PACKAGE !== 'vue3', 'Precognition is Vue-only for now')
-
test.beforeEach(async ({ page }) => {
await page.goto('/form-component/precognition')
})
From 17f4bbc4458e61b73209dcfa857131b792ab736e Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 14:54:06 +0200
Subject: [PATCH 021/145] svelte
---
packages/svelte/src/components/Form.svelte | 116 +++++++++++++++++-
.../Pages/FormComponent/Precognition.svelte | 42 +++++++
.../FormComponent/PrecognitionFiles.svelte | 52 ++++++++
.../FormComponent/PrecognitionReset.svelte | 42 +++++++
.../FormComponent/PrecognitionTouch.svelte | 48 ++++++++
.../FormComponent/PrecognitionValidate.svelte | 39 ++++++
6 files changed, 338 insertions(+), 1 deletion(-)
create mode 100644 packages/svelte/test-app/Pages/FormComponent/Precognition.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionTouch.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 194e4cf1e..04fa4437d 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -8,6 +8,7 @@
type FormDataConvertible,
type VisitOptions,
isUrlMethodPair,
+ usePrecognition,
} from '@inertiajs/core'
import { isEqual } from 'lodash-es'
import { onMount } from 'svelte'
@@ -37,6 +38,8 @@
export let resetOnError: FormComponentProps['resetOnError'] = false
export let resetOnSuccess: FormComponentProps['resetOnSuccess'] = false
export let setDefaultsOnSuccess: FormComponentProps['setDefaultsOnSuccess'] = false
+ export let validateFiles: FormComponentProps['validateFiles'] = false
+ export let validateTimeout: FormComponentProps['validateTimeout'] = 1500
type FormSubmitOptions = Omit
@@ -45,6 +48,19 @@
let isDirty = false
let defaultData: FormData = new FormData()
+ let validating = false
+ let validated: string[] = []
+ let touched: string[] = []
+
+ const validator = usePrecognition({
+ onStart: () => {
+ validating = true
+ },
+ onFinish: () => {
+ validating = false
+ },
+ })
+
$: _method = isUrlMethodPair(action) ? action.method : (method.toLowerCase() as FormComponentProps['method'])
$: _action = isUrlMethodPair(action) ? action.url : action
@@ -59,12 +75,16 @@
return formDataToObject(getFormData())
}
+ function getActionAndData(): [string, Record] {
+ return mergeDataIntoQueryString(_method, _action, getData(), queryStringArrayFormat)
+ }
+
function updateDirtyState(event: Event) {
isDirty = event.type === 'reset' ? false : !isEqual(getData(), formDataToObject(defaultData))
}
export function submit() {
- const [url, _data] = mergeDataIntoQueryString(_method, _action, getData(), queryStringArrayFormat)
+ const [url, _data] = getActionAndData()
const maybeReset = (resetOption: boolean | string[] | undefined) => {
if (!resetOption) {
@@ -133,8 +153,24 @@
}
}
+ function updateDataOnValidator() {
+ try {
+ // This might fail if the component is already unmounted but this function
+ // is called after navigating away after a form submission.
+ const data = getData()
+ validator.setOldData(data)
+ } catch {}
+ }
+
export function reset(...fields: string[]) {
resetFormFields(formElement, defaultData, fields)
+ updateDataOnValidator()
+
+ if (fields.length === 0) {
+ touched = []
+ } else {
+ touched = touched.filter((field) => !fields.includes(field))
+ }
}
export function clearErrors(...fields: string[]) {
@@ -162,17 +198,87 @@
isDirty = false
}
+ export function validate(field?: string | string[]) {
+ const only = field === undefined ? touched : Array.isArray(field) ? field : [field]
+
+ // We're not using the data object from this method as it might be empty
+ // on GET requests, and we still want to pass a data object to the
+ // validator so it knows the current state of the form.
+ const [url] = getActionAndData()
+
+ validator.validate({
+ url,
+ method: _method,
+ data: getData(),
+ only,
+ onPrecognitionSuccess: () => {
+ validated = [...validated, ...only]
+ clearErrors(...only)
+ },
+ onValidationError: (errors) => {
+ validated = [...validated, ...only]
+
+ const validFields = only.filter((field) => errors[field] === undefined)
+
+ if (validFields.length) {
+ clearErrors(...validFields)
+ }
+
+ const scopedErrors = (errorBag ? errors[errorBag || ''] || {} : errors) as Errors
+
+ // Merge current errors with new scopedErrors
+ const mergedErrors = { ...$form.errors, ...scopedErrors }
+ setError(mergedErrors)
+ },
+ })
+ }
+
+ export function touch(field: string | string[]) {
+ const fields = Array.isArray(field) ? field : [field]
+
+ // Use Set to avoid duplicates
+ touched = [...new Set([...touched, ...fields])]
+ }
+
+ function valid(field: string): boolean {
+ return validated.includes(field) && $form.errors[field] === undefined
+ }
+
+ function invalid(field: string): boolean {
+ return $form.errors[field] !== undefined
+ }
+
+ export function setValidationTimeout(duration: number) {
+ validator.setTimeout(duration)
+ }
+
+ export function validateFilesEnabled() {
+ validator.validateFiles(true)
+ }
+
onMount(() => {
defaultData = getFormData()
const formEvents = ['input', 'change', 'reset']
formEvents.forEach((e) => formElement.addEventListener(e, updateDirtyState))
+ updateDataOnValidator()
+ validator.validateFiles(validateFiles)
+ validator.setTimeout(validateTimeout)
+
return () => {
formEvents.forEach((e) => formElement?.removeEventListener(e, updateDirtyState))
}
})
+
+ $: validator.validateFiles(validateFiles)
+ $: validator.setTimeout(validateTimeout)
+
$: slotErrors = $form.errors as Errors
+
+ // Create reactive slot props that update when state changes
+ $: slotValid = (field: string) => validated.includes(field) && slotErrors[field] === undefined
+ $: slotInvalid = (field: string) => slotErrors[field] !== undefined
diff --git a/packages/svelte/test-app/Pages/FormComponent/Precognition.svelte b/packages/svelte/test-app/Pages/FormComponent/Precognition.svelte
new file mode 100644
index 000000000..c9938186c
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/Precognition.svelte
@@ -0,0 +1,42 @@
+
+
+
+
Form Precognition
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
validate('name')} />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+ {#if valid('name')}
+
Name is valid!
+ {/if}
+
+
+
+
validate('email')} />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+ {#if valid('email')}
+
Email is valid!
+ {/if}
+
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte
new file mode 100644
index 000000000..fa6569d63
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte
@@ -0,0 +1,52 @@
+
+
+
+
Form Precognition Files
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
validate('name')} />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+ {#if valid('name')}
+
Name is valid!
+ {/if}
+
+
+
+
+ {#if invalid('avatar')}
+
{errors.avatar}
+ {/if}
+ {#if valid('avatar')}
+
Avatar is valid!
+ {/if}
+
+
+ (validateFilesEnabled = !validateFilesEnabled)}>
+ Toggle Validate Files ({validateFilesEnabled ? 'enabled' : 'disabled'})
+
+
+ validate(['name', 'avatar'])}>Validate Both
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte
new file mode 100644
index 000000000..89b6dbdc0
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte
@@ -0,0 +1,42 @@
+
+
+
+
Form Precognition Reset
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
touch('name')} />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
+
+
+
touch('email')} />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+
+
+ validate()}>Validate All Touched
+ reset()}>Reset All
+ reset('name')}>Reset Name
+ reset('name', 'email')}>Reset Name and Email
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTouch.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTouch.svelte
new file mode 100644
index 000000000..ec5c641e6
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTouch.svelte
@@ -0,0 +1,48 @@
+
+
+
+
Form Precognition Touch
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
touch('name')} />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
+
+
+
touch('email')} />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+
+
+ validate()}>Validate All Touched
+ touch(['name', 'email'])}>Touch Name and Email
+ {
+ touch('name')
+ touch('name')
+ }}
+ >
+ Touch Name Twice
+
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte
new file mode 100644
index 000000000..69f4a7c44
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte
@@ -0,0 +1,39 @@
+
+
+
+
Form Precognition Validate
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
touch('name')} />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
+
+
+
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+
+
+ validate('name')}>Validate Name
+ validate(['name', 'email'])}>Validate Name and Email
+
+
From b73535c1614d3c4b1937c114e466309b09008b28 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 14:55:07 +0200
Subject: [PATCH 022/145] refactor
---
packages/react/src/Form.ts | 23 +++++++++++-----------
packages/svelte/src/components/Form.svelte | 6 +++---
packages/vue3/src/form.ts | 8 ++++----
3 files changed, 18 insertions(+), 19 deletions(-)
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 7c311971d..cd43474f5 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -104,6 +104,15 @@ const Form = forwardRef(
// expects an object, and submitting a FormData instance directly causes problems with nested objects.
const getData = (): Record => formDataToObject(getFormData())
+ const getUrlAndData = (): [string, Record] => {
+ return mergeDataIntoQueryString(
+ resolvedMethod,
+ isUrlMethodPair(action) ? action.url : action,
+ getData(),
+ queryStringArrayFormat,
+ )
+ }
+
const updateDirtyState = (event: Event) =>
deferStateUpdate(() =>
setIsDirty(event.type === 'reset' ? false : !isEqual(getData(), formDataToObject(defaultData.current))),
@@ -169,12 +178,7 @@ const Form = forwardRef(
}
const submit = () => {
- const [url, _data] = mergeDataIntoQueryString(
- resolvedMethod,
- isUrlMethodPair(action) ? action.url : action,
- getData(),
- queryStringArrayFormat,
- )
+ const [url, _data] = getUrlAndData()
const submitOptions: FormSubmitOptions = {
headers,
@@ -221,12 +225,7 @@ const Form = forwardRef(
// We're not using the data object from this method as it might be empty
// on GET requests, and we still want to pass a data object to the
// validator so it knows the current state of the form.
- const [url] = mergeDataIntoQueryString(
- resolvedMethod,
- isUrlMethodPair(action) ? action.url : action,
- getData(),
- queryStringArrayFormat,
- )
+ const [url] = getUrlAndData()
validator.validate({
url,
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 04fa4437d..e50c9d28a 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -75,7 +75,7 @@
return formDataToObject(getFormData())
}
- function getActionAndData(): [string, Record] {
+ function getUrlAndData(): [string, Record] {
return mergeDataIntoQueryString(_method, _action, getData(), queryStringArrayFormat)
}
@@ -84,7 +84,7 @@
}
export function submit() {
- const [url, _data] = getActionAndData()
+ const [url, _data] = getUrlAndData()
const maybeReset = (resetOption: boolean | string[] | undefined) => {
if (!resetOption) {
@@ -204,7 +204,7 @@
// We're not using the data object from this method as it might be empty
// on GET requests, and we still want to pass a data object to the
// validator so it knows the current state of the form.
- const [url] = getActionAndData()
+ const [url] = getUrlAndData()
validator.validate({
url,
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 139b12948..656338db3 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -179,7 +179,7 @@ const Form: InertiaForm = defineComponent({
// expects an object, and submitting a FormData instance directly causes problems with nested objects.
const getData = (): Record => formDataToObject(getFormData())
- const getActionAndData = (): [string, Record] => {
+ const getUrlAndData = (): [string, Record] => {
return mergeDataIntoQueryString(
method.value,
isUrlMethodPair(props.action) ? props.action.url : props.action,
@@ -228,10 +228,10 @@ const Form: InertiaForm = defineComponent({
...props.options,
}
- const [action, data] = getActionAndData()
+ const [url, data] = getUrlAndData()
// We need transform because we can't override the default data with different keys (by design)
- form.transform(() => props.transform(data)).submit(method.value, action, submitOptions)
+ form.transform(() => props.transform(data)).submit(method.value, url, submitOptions)
}
const updateDataOnValidator = () => {
@@ -270,7 +270,7 @@ const Form: InertiaForm = defineComponent({
// We're not using the data object from this method as it might be empty
// on GET requests, and we still want to pass a data object to the
// validator so it knows the current state of the form.
- const [url] = getActionAndData()
+ const [url] = getUrlAndData()
validator.validate({
url,
From cea03dbaae5477c33664a93acacf569f43290979 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 15:28:56 +0200
Subject: [PATCH 023/145] transform + callbacks
---
packages/core/src/precognition.ts | 12 ++--
packages/core/src/types.ts | 7 ++
packages/react/src/Form.ts | 42 ++++++++---
.../FormComponent/PrecognitionCallbacks.tsx | 63 +++++++++++++++++
.../FormComponent/PrecognitionTransform.tsx | 28 ++++++++
packages/svelte/src/components/Form.svelte | 44 ++++++++----
.../PrecognitionCallbacks.svelte | 64 +++++++++++++++++
.../PrecognitionTransform.svelte | 33 +++++++++
packages/vue3/src/form.ts | 42 ++++++++---
.../FormComponent/PrecognitionCallbacks.vue | 69 +++++++++++++++++++
.../FormComponent/PrecognitionTransform.vue | 26 +++++++
playgrounds/vue3/package.json | 2 +-
tests/app/server.js | 38 ++++++++--
tests/form-component.spec.ts | 41 +++++++++++
14 files changed, 466 insertions(+), 45 deletions(-)
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index d1bf0987e..c39dc2c6b 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -3,7 +3,7 @@ import { get, isEqual } from 'lodash-es'
import debounce from './debounce'
import { forgetFiles, hasFiles } from './files'
import { objectToFormData } from './formData'
-import { ErrorBag, Errors, Method, RequestData } from './types'
+import { Errors, Method, RequestData } from './types'
interface UsePrecognitionOptions {
onStart: () => void
@@ -15,9 +15,10 @@ interface PrecognitionValidateOptions {
method: Method
data: RequestData
only: string[]
- errorBag?: string
+ errorBag?: string | null
onPrecognitionSuccess: () => void
- onValidationError: (errors: Errors & ErrorBag) => void
+ onValidationError: (errors: Errors) => void
+ onFinish?: () => void
}
interface PrecognitionValidator {
@@ -71,13 +72,16 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
})
.catch((error) => {
if (error.response?.status === 422) {
- return options.onValidationError(error.response.data?.errors || {})
+ const errors = error.response.data?.errors || {}
+ const scopedErrors = (options.errorBag ? errors[options.errorBag] || {} : errors) as Errors
+ return options.onValidationError(scopedErrors)
}
throw error
})
.finally(() => {
oldData = { ...data }
+ options.onFinish?.()
precognitionOptions.onFinish()
})
},
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 7f49c116c..82c2f4af0 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -499,6 +499,13 @@ export type FormComponentPrecognition = {
validating: boolean
}
+export type FormComponentPrecognitionValidateOptions = {
+ only?: string | string[]
+ onSuccess?: () => void
+ onError?: (errors: Errors) => void
+ onFinish?: () => void
+}
+
export type FormComponentonSubmitCompleteArguments = Pick
export type FormComponentState = {
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index cd43474f5..c4a6c46b2 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -1,6 +1,6 @@
import {
- Errors,
FormComponentPrecognition,
+ FormComponentPrecognitionValidateOptions,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
@@ -219,8 +219,23 @@ const Form = forwardRef(
setIsDirty(false)
}
- const validate = (field?: string | string[]) => {
- const only = field === undefined ? touched : Array.isArray(field) ? field : [field]
+ const validate = (
+ only?: string | string[] | FormComponentPrecognitionValidateOptions,
+ maybeOptions?: FormComponentPrecognitionValidateOptions,
+ ) => {
+ let fields: string[]
+ let options: FormComponentPrecognitionValidateOptions = {}
+
+ if (typeof only === 'object' && !Array.isArray(only)) {
+ // Called as validate({ only: [...], onSuccess, onError, onFinish })
+ const onlyFields = only.only
+ fields = onlyFields === undefined ? touched : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
+ options = only
+ } else {
+ // Called as validate('field') or validate(['field1', 'field2']) or validate('field', {options})
+ fields = only === undefined ? touched : Array.isArray(only) ? only : [only]
+ options = maybeOptions || {}
+ }
// We're not using the data object from this method as it might be empty
// on GET requests, and we still want to pass a data object to the
@@ -230,23 +245,28 @@ const Form = forwardRef(
validator.validate({
url,
method: resolvedMethod,
- data: getData(),
- only,
+ data: transform(getData()),
+ only: fields,
+ errorBag,
onPrecognitionSuccess: () => {
- setValidated((prev) => [...prev, ...only])
- form.clearErrors(...only)
+ setValidated((prev) => [...prev, ...fields])
+ form.clearErrors(...fields)
+ options.onSuccess?.()
},
onValidationError: (errors) => {
- setValidated((prev) => [...prev, ...only])
+ setValidated((prev) => [...prev, ...fields])
- const validFields = only.filter((field) => errors[field] === undefined)
+ const validFields = fields.filter((field) => errors[field] === undefined)
if (validFields.length) {
form.clearErrors(...validFields)
}
- const scopedErrors = (errorBag ? errors[errorBag || ''] || {} : errors) as Errors
- form.setError({ ...form.errors, ...scopedErrors })
+ form.setError({ ...form.errors, ...errors })
+ options.onError?.(errors)
+ },
+ onFinish: () => {
+ options.onFinish?.()
},
})
}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
new file mode 100644
index 000000000..bc418536b
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
@@ -0,0 +1,63 @@
+import { Form } from '@inertiajs/react'
+import { useState } from 'react'
+
+export default () => {
+ const [successCalled, setSuccessCalled] = useState(false)
+ const [errorCalled, setErrorCalled] = useState(false)
+ const [finishCalled, setFinishCalled] = useState(false)
+
+ return (
+
+
Form Precognition Callbacks
+
+
+ {({ validate, validating, touch }) => (
+ <>
+ {validating && Validating...
}
+ {successCalled && onSuccess called!
}
+ {errorCalled && onError called!
}
+ {finishCalled && onFinish called!
}
+
+
+ touch('name')} />
+
+
+ {
+ setSuccessCalled(false)
+ setErrorCalled(false)
+ setFinishCalled(false)
+ validate({
+ onSuccess: () => {
+ setSuccessCalled(true)
+ setFinishCalled(true)
+ },
+ })
+ }}
+ >
+ Validate with onSuccess
+
+
+ {
+ setSuccessCalled(false)
+ setErrorCalled(false)
+ setFinishCalled(false)
+ validate({
+ onError: () => {
+ setErrorCalled(true)
+ setFinishCalled(true)
+ },
+ })
+ }}
+ >
+ Validate with onError
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
new file mode 100644
index 000000000..5d43bf8a0
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
@@ -0,0 +1,28 @@
+import { Form } from '@inertiajs/react'
+
+export default () => {
+ return (
+
+
Form Precognition Transform
+
+
({ name: data.name.toUpperCase() })}
+ >
+ {({ invalid, errors, validate, valid, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
validate('name')} />
+ {invalid('name') &&
{errors.name}
}
+ {valid('name') &&
Name is valid!
}
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index e50c9d28a..d3ee93be1 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -6,6 +6,7 @@
type Errors,
type FormComponentProps,
type FormDataConvertible,
+ type FormComponentPrecognitionValidateOptions,
type VisitOptions,
isUrlMethodPair,
usePrecognition,
@@ -198,8 +199,23 @@
isDirty = false
}
- export function validate(field?: string | string[]) {
- const only = field === undefined ? touched : Array.isArray(field) ? field : [field]
+ export function validate(
+ only?: string | string[] | FormComponentPrecognitionValidateOptions,
+ maybeOptions?: FormComponentPrecognitionValidateOptions,
+ ) {
+ let fields: string[]
+ let options: FormComponentPrecognitionValidateOptions = {}
+
+ if (typeof only === 'object' && !Array.isArray(only)) {
+ // Called as validate({ only: [...], onSuccess, onError, onFinish })
+ const onlyFields = only.only
+ fields = onlyFields === undefined ? touched : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
+ options = only
+ } else {
+ // Called as validate('field') or validate(['field1', 'field2']) or validate('field', {options})
+ fields = only === undefined ? touched : Array.isArray(only) ? only : [only]
+ options = maybeOptions || {}
+ }
// We're not using the data object from this method as it might be empty
// on GET requests, and we still want to pass a data object to the
@@ -209,26 +225,30 @@
validator.validate({
url,
method: _method,
- data: getData(),
- only,
+ data: transform(getData()),
+ only: fields,
+ errorBag,
onPrecognitionSuccess: () => {
- validated = [...validated, ...only]
- clearErrors(...only)
+ validated = [...validated, ...fields]
+ clearErrors(...fields)
+ options.onSuccess?.()
},
onValidationError: (errors) => {
- validated = [...validated, ...only]
+ validated = [...validated, ...fields]
- const validFields = only.filter((field) => errors[field] === undefined)
+ const validFields = fields.filter((field) => errors[field] === undefined)
if (validFields.length) {
clearErrors(...validFields)
}
- const scopedErrors = (errorBag ? errors[errorBag || ''] || {} : errors) as Errors
-
- // Merge current errors with new scopedErrors
- const mergedErrors = { ...$form.errors, ...scopedErrors }
+ // Merge current errors with new errors
+ const mergedErrors = { ...$form.errors, ...errors }
setError(mergedErrors)
+ options.onError?.(errors)
+ },
+ onFinish: () => {
+ options.onFinish?.()
},
})
}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
new file mode 100644
index 000000000..acd97449e
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
@@ -0,0 +1,64 @@
+
+
+
+
Form Precognition Callbacks
+
+
+ {#if validating}
+ Validating...
+ {/if}
+ {#if successCalled}
+ onSuccess called!
+ {/if}
+ {#if errorCalled}
+ onError called!
+ {/if}
+ {#if finishCalled}
+ onFinish called!
+ {/if}
+
+
+ touch('name')} />
+
+
+ {
+ successCalled = false
+ errorCalled = false
+ finishCalled = false
+ validate({
+ onSuccess: () => {
+ successCalled = true
+ finishCalled = true
+ },
+ })
+ }}
+ >
+ Validate with onSuccess
+
+
+ {
+ successCalled = false
+ errorCalled = false
+ finishCalled = false
+ validate({
+ onError: () => {
+ errorCalled = true
+ finishCalled = true
+ },
+ })
+ }}
+ >
+ Validate with onError
+
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
new file mode 100644
index 000000000..87591c8cf
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
@@ -0,0 +1,33 @@
+
+
+
+
Form Precognition Transform
+
+
({ name: data.name.toUpperCase() })}
+ let:invalid
+ let:errors
+ let:validate
+ let:valid
+ let:validating
+ >
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
validate('name')} />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+ {#if valid('name')}
+
Name is valid!
+ {/if}
+
+
+
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 656338db3..dd3de777b 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -1,6 +1,6 @@
import {
- Errors,
FormComponentPrecognition,
+ FormComponentPrecognitionValidateOptions,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
@@ -264,8 +264,23 @@ const Form: InertiaForm = defineComponent({
isDirty.value = false
}
- const validate = (field?: string | string[]) => {
- const only = field === undefined ? touched.value : Array.isArray(field) ? field : [field]
+ const validate = (
+ only?: string | string[] | FormComponentPrecognitionValidateOptions,
+ maybeOptions?: FormComponentPrecognitionValidateOptions,
+ ) => {
+ let fields: string[]
+ let options: FormComponentPrecognitionValidateOptions = {}
+
+ if (typeof only === 'object' && !Array.isArray(only)) {
+ // Called as validate({ only: [...], onSuccess, onError, onFinish })
+ const onlyFields = only.only
+ fields = onlyFields === undefined ? touched.value : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
+ options = only
+ } else {
+ // Called as validate('field') or validate(['field1', 'field2']) or validate('field', {options})
+ fields = only === undefined ? touched.value : Array.isArray(only) ? only : [only]
+ options = maybeOptions || {}
+ }
// We're not using the data object from this method as it might be empty
// on GET requests, and we still want to pass a data object to the
@@ -275,23 +290,28 @@ const Form: InertiaForm = defineComponent({
validator.validate({
url,
method: method.value,
- data: getData(),
- only,
+ data: props.transform(getData()),
+ only: fields,
+ errorBag: props.errorBag,
onPrecognitionSuccess: () => {
- validated.value = [...validated.value, ...only]
- form.clearErrors(...only)
+ validated.value = [...validated.value, ...fields]
+ form.clearErrors(...fields)
+ options.onSuccess?.()
},
onValidationError: (errors) => {
- validated.value = [...validated.value, ...only]
+ validated.value = [...validated.value, ...fields]
- const validFields = only.filter((field) => errors[field] === undefined)
+ const validFields = fields.filter((field) => errors[field] === undefined)
if (validFields.length) {
form.clearErrors(...validFields)
}
- const scopedErrors = (props.errorBag ? errors[props.errorBag || ''] || {} : errors) as Errors
- form.setError({ ...form.errors, ...scopedErrors })
+ form.setError({ ...form.errors, ...errors })
+ options.onError?.(errors)
+ },
+ onFinish: () => {
+ options.onFinish?.()
},
})
}
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
new file mode 100644
index 000000000..3494406e9
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
Form Precognition Callbacks
+
+
+
+ Validating...
+ onSuccess called!
+ onError called!
+ onFinish called!
+
+
+ touch('name')" />
+
+
+ {
+ successCalled = false
+ errorCalled = false
+ finishCalled = false
+ validate({
+ onSuccess: () => {
+ successCalled = true
+ },
+ onFinish: () => {
+ finishCalled = true
+ },
+ })
+ }
+ "
+ >
+ Validate with onSuccess
+
+
+ {
+ successCalled = false
+ errorCalled = false
+ finishCalled = false
+ validate({
+ onError: () => {
+ errorCalled = true
+ },
+ onFinish: () => {
+ finishCalled = true
+ },
+ })
+ }
+ "
+ >
+ Validate with onError
+
+
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
new file mode 100644
index 000000000..580a882ac
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
Form Precognition Transform
+
+
+
+ Validating...
+
+
+
validate('name')" />
+
{{ errors.name }}
+
Name is valid!
+
+
+
+
+
diff --git a/playgrounds/vue3/package.json b/playgrounds/vue3/package.json
index d36c26e42..e089cc932 100644
--- a/playgrounds/vue3/package.json
+++ b/playgrounds/vue3/package.json
@@ -24,4 +24,4 @@
"vue-tsc": "^2.2.12"
},
"type": "module"
-}
\ No newline at end of file
+}
diff --git a/tests/app/server.js b/tests/app/server.js
index fdf433854..653e1946b 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -928,12 +928,6 @@ app.post('/form-component/precognition', (req, res) => {
}, 500)
})
-app.get('/form-component/precognition-files', (req, res) => {
- inertia.render(req, res, {
- component: 'FormComponent/PrecognitionFiles',
- })
-})
-
app.post('/form-component/precognition-files', upload.any(), (req, res) => {
setTimeout(() => {
console.log(req, req)
@@ -973,6 +967,38 @@ app.post('/form-component/precognition-files', upload.any(), (req, res) => {
}, 500)
})
+app.post('/form-component/precognition-transform', (req, res) => {
+ setTimeout(() => {
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const errors = {}
+
+ // Validate that name is uppercase
+ if (!name) {
+ errors.name = 'The name field is required.'
+ } else if (name !== name.toUpperCase()) {
+ errors.name = 'The name must be uppercase.'
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 500)
+})
+
//
function renderInfiniteScroll(req, res, component, total = 40, orderByDesc = false, perPage = 15) {
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 7a9d9c214..0a3e185bb 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1793,6 +1793,47 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
await expect(page.getByText('The avatar field is required.')).not.toBeVisible()
})
+
+ test('transforms data for validation requests', async ({ page }) => {
+ await page.goto('/form-component/precognition-transform')
+
+ await page.fill('input[name="name"]', 'taylor')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should succeed because transform converts to uppercase
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ })
+
+ test('calls onSuccess and onFinish callbacks when validation succeeds', async ({ page }) => {
+ await page.goto('/form-component/precognition-callbacks')
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.click('button:has-text("Validate with onSuccess")')
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('onSuccess called!')).toBeVisible()
+ await expect(page.getByText('onError called!')).not.toBeVisible()
+ await expect(page.getByText('onFinish called!')).toBeVisible()
+ })
+
+ test('calls onError and onFinish callbacks when validation fails', async ({ page }) => {
+ await page.goto('/form-component/precognition-callbacks')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.click('button:has-text("Validate with onError")')
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('onSuccess called!')).not.toBeVisible()
+ await expect(page.getByText('onError called!')).toBeVisible()
+ await expect(page.getByText('onFinish called!')).toBeVisible()
+ })
})
test.describe('React', () => {
From 0da076084b3fdfcea7b81b2d0dd8e0f198fe138d Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 15:31:43 +0200
Subject: [PATCH 024/145] types
---
.../test-app/Pages/FormComponent/PrecognitionTransform.tsx | 2 +-
.../test-app/Pages/FormComponent/PrecognitionTransform.svelte | 2 +-
.../vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
index 5d43bf8a0..4eb783f61 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
@@ -9,7 +9,7 @@ export default () => {
action="/form-component/precognition-transform"
method="post"
validateTimeout={100}
- transform={(data) => ({ name: data.name.toUpperCase() })}
+ transform={(data) => ({ name: String(data.name || '').toUpperCase() })}
>
{({ invalid, errors, validate, valid, validating }) => (
<>
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
index 87591c8cf..265f17308 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
@@ -9,7 +9,7 @@
action="/form-component/precognition-transform"
method="post"
validateTimeout={100}
- transform={(data) => ({ name: data.name.toUpperCase() })}
+ transform={(data) => ({ name: String(data.name || '').toUpperCase() })}
let:invalid
let:errors
let:validate
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
index 580a882ac..8f9bb6cd4 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
@@ -10,7 +10,7 @@ import { Form } from '@inertiajs/vue3'
action="/form-component/precognition-transform"
method="post"
:validateTimeout="100"
- :transform="(data) => ({ name: data.name.toUpperCase() })"
+ :transform="(data) => ({ name: String(data.name || '').toUpperCase() })"
>
Validating...
From 4c735d38c7954f9de0984de7cf073ef2f9f4a89a Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 16:57:29 +0200
Subject: [PATCH 025/145] Update types.ts
---
packages/core/src/types.ts | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 82c2f4af0..8277e6a93 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -491,14 +491,6 @@ export type FormComponentMethods = {
defaults: () => void
}
-export type FormComponentPrecognition = {
- valid: (field: string) => boolean
- invalid: (field: string) => boolean
- validate: (field?: string | string[]) => void
- touch: (field: string | string[]) => void
- validating: boolean
-}
-
export type FormComponentPrecognitionValidateOptions = {
only?: string | string[]
onSuccess?: () => void
@@ -506,6 +498,17 @@ export type FormComponentPrecognitionValidateOptions = {
onFinish?: () => void
}
+export type FormComponentPrecognition = {
+ valid: (field: string) => boolean
+ invalid: (field: string) => boolean
+ validate: (
+ only?: string | string[] | FormComponentPrecognitionValidateOptions,
+ maybeOptions?: FormComponentPrecognitionValidateOptions,
+ ) => void
+ touch: (field: string | string[]) => void
+ validating: boolean
+}
+
export type FormComponentonSubmitCompleteArguments = Pick
export type FormComponentState = {
From 3e7599816070189ac6829d988e8e077be8a5456e Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 17:05:58 +0200
Subject: [PATCH 026/145] refactor
---
packages/core/src/precognition.ts | 10 ++++------
packages/core/src/types.ts | 23 ++++++++++------------
packages/react/src/Form.ts | 11 +++++------
packages/svelte/src/components/Form.svelte | 8 ++++----
packages/vue3/src/form.ts | 13 ++++++------
5 files changed, 29 insertions(+), 36 deletions(-)
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index c39dc2c6b..18c392027 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -34,7 +34,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
let oldData: RequestData = {}
- const setDebounceTimeout = (value: number) => {
+ const setTimeout = (value: number) => {
if (value !== debounceTimeoutDuration) {
debounceTimeoutDuration = value
validate = createValidateFunction()
@@ -92,15 +92,13 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
let validate = createValidateFunction()
return {
- setOldData(data: RequestData) {
+ setOldData: (data) => {
oldData = { ...data }
},
-
- validateFiles(value: boolean) {
+ validateFiles: (value) => {
validateFiles = value
},
-
+ setTimeout,
validate,
- setTimeout: setDebounceTimeout,
}
}
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 8277e6a93..971e7f7f5 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -481,6 +481,13 @@ export type FormComponentProps = Partial<
validateTimeout?: number
}
+export type FormComponentValidateOptions = {
+ only?: string | string[]
+ onSuccess?: () => void
+ onError?: (errors: Errors) => void
+ onFinish?: () => void
+}
+
export type FormComponentMethods = {
clearErrors: (...fields: string[]) => void
resetAndClearErrors: (...fields: string[]) => void
@@ -489,24 +496,13 @@ export type FormComponentMethods = {
reset: (...fields: string[]) => void
submit: () => void
defaults: () => void
-}
-
-export type FormComponentPrecognitionValidateOptions = {
- only?: string | string[]
- onSuccess?: () => void
- onError?: (errors: Errors) => void
- onFinish?: () => void
-}
-
-export type FormComponentPrecognition = {
valid: (field: string) => boolean
invalid: (field: string) => boolean
validate: (
- only?: string | string[] | FormComponentPrecognitionValidateOptions,
- maybeOptions?: FormComponentPrecognitionValidateOptions,
+ only?: string | string[] | FormComponentValidateOptions,
+ maybeOptions?: FormComponentValidateOptions,
) => void
touch: (field: string | string[]) => void
- validating: boolean
}
export type FormComponentonSubmitCompleteArguments = Pick
@@ -519,6 +515,7 @@ export type FormComponentState = {
wasSuccessful: boolean
recentlySuccessful: boolean
isDirty: boolean
+ validating: boolean
}
export type FormComponentSlotProps = FormComponentMethods & FormComponentState
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index c4a6c46b2..18b6de2de 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -1,9 +1,8 @@
import {
- FormComponentPrecognition,
- FormComponentPrecognitionValidateOptions,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
+ FormComponentValidateOptions,
FormDataConvertible,
formDataToObject,
isUrlMethodPair,
@@ -35,7 +34,7 @@ const deferStateUpdate = (callback: () => void) => {
type ComponentProps = (FormComponentProps &
Omit, keyof FormComponentProps | 'children'> &
Omit, keyof FormComponentProps | 'children'>) & {
- children: ReactNode | ((props: FormComponentSlotProps & FormComponentPrecognition) => ReactNode)
+ children: ReactNode | ((props: FormComponentSlotProps) => ReactNode)
}
type FormSubmitOptions = Omit
@@ -220,11 +219,11 @@ const Form = forwardRef(
}
const validate = (
- only?: string | string[] | FormComponentPrecognitionValidateOptions,
- maybeOptions?: FormComponentPrecognitionValidateOptions,
+ only?: string | string[] | FormComponentValidateOptions,
+ maybeOptions?: FormComponentValidateOptions,
) => {
let fields: string[]
- let options: FormComponentPrecognitionValidateOptions = {}
+ let options: FormComponentValidateOptions = {}
if (typeof only === 'object' && !Array.isArray(only)) {
// Called as validate({ only: [...], onSuccess, onError, onFinish })
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index d3ee93be1..e5d96854e 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -6,7 +6,7 @@
type Errors,
type FormComponentProps,
type FormDataConvertible,
- type FormComponentPrecognitionValidateOptions,
+ type FormComponentValidateOptions,
type VisitOptions,
isUrlMethodPair,
usePrecognition,
@@ -200,11 +200,11 @@
}
export function validate(
- only?: string | string[] | FormComponentPrecognitionValidateOptions,
- maybeOptions?: FormComponentPrecognitionValidateOptions,
+ only?: string | string[] | FormComponentValidateOptions,
+ maybeOptions?: FormComponentValidateOptions,
) {
let fields: string[]
- let options: FormComponentPrecognitionValidateOptions = {}
+ let options: FormComponentValidateOptions = {}
if (typeof only === 'object' && !Array.isArray(only)) {
// Called as validate({ only: [...], onSuccess, onError, onFinish })
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index dd3de777b..1c3940818 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -1,9 +1,8 @@
import {
- FormComponentPrecognition,
- FormComponentPrecognitionValidateOptions,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
+ FormComponentValidateOptions,
FormDataConvertible,
formDataToObject,
isUrlMethodPair,
@@ -265,11 +264,11 @@ const Form: InertiaForm = defineComponent({
}
const validate = (
- only?: string | string[] | FormComponentPrecognitionValidateOptions,
- maybeOptions?: FormComponentPrecognitionValidateOptions,
+ only?: string | string[] | FormComponentValidateOptions,
+ maybeOptions?: FormComponentValidateOptions,
) => {
let fields: string[]
- let options: FormComponentPrecognitionValidateOptions = {}
+ let options: FormComponentValidateOptions = {}
if (typeof only === 'object' && !Array.isArray(only)) {
// Called as validate({ only: [...], onSuccess, onError, onFinish })
@@ -365,7 +364,7 @@ const Form: InertiaForm = defineComponent({
validateFiles: () => validator.validateFiles(true),
}
- expose(exposed)
+ expose(exposed)
return () => {
return h(
@@ -381,7 +380,7 @@ const Form: InertiaForm = defineComponent({
},
inert: props.disableWhileProcessing && form.processing,
},
- slots.default ? slots.default(exposed) : [],
+ slots.default ? slots.default(exposed) : [],
)
}
},
From 4a59c169ce8fbaddd810286c2a6422f59eab6ac5 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 17:34:50 +0200
Subject: [PATCH 027/145] Revert playground
---
.../app/Http/Requests/StoreUserRequest.php | 21 -------------------
playgrounds/vue3/routes/web.php | 6 ------
2 files changed, 27 deletions(-)
delete mode 100644 playgrounds/vue3/app/Http/Requests/StoreUserRequest.php
diff --git a/playgrounds/vue3/app/Http/Requests/StoreUserRequest.php b/playgrounds/vue3/app/Http/Requests/StoreUserRequest.php
deleted file mode 100644
index 392fa0763..000000000
--- a/playgrounds/vue3/app/Http/Requests/StoreUserRequest.php
+++ /dev/null
@@ -1,21 +0,0 @@
-|string>
- */
- public function rules(): array
- {
- return [
- 'name' => ['required', 'string', 'min:3', 'max:255'],
- 'email' => ['required', 'string', 'email', 'max:255'],
- ];
- }
-}
diff --git a/playgrounds/vue3/routes/web.php b/playgrounds/vue3/routes/web.php
index b51be2dba..efa24a69e 100644
--- a/playgrounds/vue3/routes/web.php
+++ b/playgrounds/vue3/routes/web.php
@@ -1,8 +1,6 @@
middleware([HandlePrecognitiveRequests::class]);
-
Route::get('/users', function () {
sleep(2);
From 0733ab007c7550b18214ade5e02aa5e90d83cd69 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 3 Oct 2025 17:34:56 +0200
Subject: [PATCH 028/145] Update Form.svelte
---
packages/svelte/src/components/Form.svelte | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index e5d96854e..6fe9cb405 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -260,11 +260,11 @@
touched = [...new Set([...touched, ...fields])]
}
- function valid(field: string): boolean {
+ export function valid(field: string): boolean {
return validated.includes(field) && $form.errors[field] === undefined
}
- function invalid(field: string): boolean {
+ export function invalid(field: string): boolean {
return $form.errors[field] !== undefined
}
From ec8b445a1687e1d0fc6fb8984c6a75b9c6d75adc Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 7 Oct 2025 13:09:03 +0200
Subject: [PATCH 029/145] Used transformed data for validation
---
packages/react/src/Form.ts | 3 +--
packages/svelte/src/components/Form.svelte | 3 +--
packages/vue3/src/form.ts | 3 +--
3 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 18b6de2de..a047671c5 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -143,8 +143,7 @@ const Form = forwardRef(
try {
// This might fail if the component is already unmounted but this function
// is called after navigating away after a form submission.
- const data = getData()
- validator.setOldData(data)
+ validator.setOldData(transform(getData()))
} catch {}
}
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 6fe9cb405..842b39626 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -158,8 +158,7 @@
try {
// This might fail if the component is already unmounted but this function
// is called after navigating away after a form submission.
- const data = getData()
- validator.setOldData(data)
+ validator.setOldData(transform(getData()))
} catch {}
}
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 1c3940818..1845a6c9b 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -237,8 +237,7 @@ const Form: InertiaForm = defineComponent({
try {
// This might fail if the component is already unmounted but this function
// is called after navigating away after a form submission.
- const data = getData()
- validator.setOldData(data)
+ validator.setOldData(props.transform(getData()))
} catch {}
}
From 361c22b87dbbcbb1f0ded1b0fece8273cce2e5f7 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 7 Oct 2025 14:25:32 +0200
Subject: [PATCH 030/145] wip
---
packages/core/src/precognition.ts | 57 +++++++--
packages/core/src/types.ts | 7 ++
packages/react/src/Form.ts | 1 +
packages/svelte/src/components/Form.svelte | 1 +
packages/vue3/src/form.ts | 4 +
.../PrecognitionBeforeValidation.vue | 65 ++++++++++
.../PrecognitionBeforeValidationPerCall.vue | 59 +++++++++
.../FormComponent/PrecognitionCancel.vue | 27 +++++
.../FormComponent/PrecognitionDefaults.vue | 36 ++++++
.../FormComponent/PrecognitionException.vue | 41 +++++++
.../FormComponent/PrecognitionHeaders.vue | 28 +++++
tests/app/server.js | 32 +++++
tests/form-component.spec.ts | 114 ++++++++++++++++++
13 files changed, 461 insertions(+), 11 deletions(-)
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index 18c392027..eba2ca16f 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -3,23 +3,19 @@ import { get, isEqual } from 'lodash-es'
import debounce from './debounce'
import { forgetFiles, hasFiles } from './files'
import { objectToFormData } from './formData'
-import { Errors, Method, RequestData } from './types'
+import { Errors, FormComponentValidateOptions, RequestData, Visit } from './types'
interface UsePrecognitionOptions {
onStart: () => void
onFinish: () => void
}
-interface PrecognitionValidateOptions {
- url: string
- method: Method
- data: RequestData
- only: string[]
- errorBag?: string | null
- onPrecognitionSuccess: () => void
- onValidationError: (errors: Errors) => void
- onFinish?: () => void
-}
+type PrecognitionValidateOptions = Pick, 'method' | 'data' | 'only' | 'errorBag' | 'headers'> &
+ Pick & {
+ url: string
+ onPrecognitionSuccess: () => void
+ onValidationError: (errors: Errors) => void
+ }
interface PrecognitionValidator {
setOldData: (data: RequestData) => void
@@ -33,14 +29,26 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
let validateFiles: boolean = false
let oldData: RequestData = {}
+ let oldTouched: string[] = []
+ const abortControllers: Record = {}
+
+ const cancelAll = () => {
+ Object.values(abortControllers).forEach((controller) => controller.abort())
+ Object.keys(abortControllers).forEach((key) => delete abortControllers[key])
+ }
const setTimeout = (value: number) => {
if (value !== debounceTimeoutDuration) {
+ cancelAll()
debounceTimeoutDuration = value
validate = createValidateFunction()
}
}
+ const createFingerprint = (options: PrecognitionValidateOptions) => {
+ return `${options.method}:${options.url}`
+ }
+
const createValidateFunction = () =>
debounce(
(options: PrecognitionValidateOptions) => {
@@ -51,13 +59,33 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
return
}
+ const beforeValidatonResult = options.onBeforeValidation?.(
+ { data: options.data, touched: options.only },
+ { data: oldData, touched: oldTouched },
+ )
+
+ if (beforeValidatonResult === false) {
+ return
+ }
+
+ const fingerprint = createFingerprint(options)
+
+ if (abortControllers[fingerprint]) {
+ abortControllers[fingerprint].abort()
+ delete abortControllers[fingerprint]
+ }
+
+ abortControllers[fingerprint] = new AbortController()
+
precognitionOptions.onStart()
const submitOptions: AxiosRequestConfig = {
method: options.method,
url: options.url,
data: hasFiles(data) ? objectToFormData(data) : { ...data },
+ signal: abortControllers[fingerprint].signal,
headers: {
+ ...(options.headers || {}),
'X-Requested-With': 'XMLHttpRequest',
Precognition: true,
...(options.only.length ? { 'Precognition-Validate-Only': options.only.join(',') } : {}),
@@ -77,10 +105,17 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
return options.onValidationError(scopedErrors)
}
+ if (options.onException) {
+ options.onException(error)
+ return
+ }
+
throw error
})
.finally(() => {
oldData = { ...data }
+ oldTouched = [...options.only]
+ delete abortControllers[fingerprint]
options.onFinish?.()
precognitionOptions.onFinish()
})
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 971e7f7f5..a2ef1214d 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -481,11 +481,18 @@ export type FormComponentProps = Partial<
validateTimeout?: number
}
+type OnBeforeValidationCallback = (
+ newRequest: { data: RequestData; touched: string[] },
+ oldRequest: { data: RequestData; touched: string[] },
+) => boolean | undefined
+
export type FormComponentValidateOptions = {
only?: string | string[]
onSuccess?: () => void
onError?: (errors: Errors) => void
onFinish?: () => void
+ onBeforeValidation?: OnBeforeValidationCallback
+ onException?: (error: Error) => void
}
export type FormComponentMethods = {
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index a047671c5..bdd1a2340 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -246,6 +246,7 @@ const Form = forwardRef(
data: transform(getData()),
only: fields,
errorBag,
+ headers,
onPrecognitionSuccess: () => {
setValidated((prev) => [...prev, ...fields])
form.clearErrors(...fields)
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 842b39626..4fc149f21 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -227,6 +227,7 @@
data: transform(getData()),
only: fields,
errorBag,
+ headers,
onPrecognitionSuccess: () => {
validated = [...validated, ...fields]
clearErrors(...fields)
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 1845a6c9b..920f6aefe 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -260,6 +260,7 @@ const Form: InertiaForm = defineComponent({
const defaults = () => {
defaultData.value = getFormData()
isDirty.value = false
+ updateDataOnValidator()
}
const validate = (
@@ -291,6 +292,8 @@ const Form: InertiaForm = defineComponent({
data: props.transform(getData()),
only: fields,
errorBag: props.errorBag,
+ headers: props.headers,
+ onBeforeValidation: options.onBeforeValidation,
onPrecognitionSuccess: () => {
validated.value = [...validated.value, ...fields]
form.clearErrors(...fields)
@@ -308,6 +311,7 @@ const Form: InertiaForm = defineComponent({
form.setError({ ...form.errors, ...errors })
options.onError?.(errors)
},
+ onException: options.onException,
onFinish: () => {
options.onFinish?.()
},
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
new file mode 100644
index 000000000..2505d97e9
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
@@ -0,0 +1,65 @@
+
+
+
Precognition - onBeforeValidation
+
+
+
+
Name:
+
+
{{ errors.name }}
+
+
+
+
Email:
+
+
{{ errors.email }}
+
+
+ Validating...
+ Validation blocked by onBeforeValidation
+ Data structure is correct
+
+ Submit
+
+
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue
new file mode 100644
index 000000000..d5d5fe11a
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
Precognition - onBeforeValidation Per Call
+
+
+ Validating...
+ Blocked by first callback
+ Blocked by second callback
+
+
+
+
+
+
+
+ Validate with First
+
+
+
+
+ Validate with Second
+
+
+ Submit
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
new file mode 100644
index 000000000..6b65b3c3b
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
Precognition - Auto Cancel
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
+
+ Submit
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
new file mode 100644
index 000000000..8fa996cc5
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
Precognition - Defaults Updates Validator
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
+
+ Set Defaults
+ Submit
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue
new file mode 100644
index 000000000..a4039fe9e
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
Precognition - onException
+
+
+ Validating...
+ Exception caught: {{ exceptionMessage }}
+
+
+
+
+
+
+
+ Validate with Exception Handler
+
+
+ Submit
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue
new file mode 100644
index 000000000..bcb0dbbdd
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
Precognition - Custom Headers
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
+
+ Submit
+
+
+
diff --git a/tests/app/server.js b/tests/app/server.js
index 653e1946b..de198fb91 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -999,6 +999,38 @@ app.post('/form-component/precognition-transform', (req, res) => {
}, 500)
})
+app.post('/form-component/precognition-headers', (req, res) => {
+ setTimeout(() => {
+ const customHeader = req.headers['x-custom-header']
+ const name = req.body['name']
+ const errors = {}
+
+ // Validate that custom header is present
+ if (customHeader !== 'custom-value') {
+ errors.name = 'Custom header missing or incorrect.'
+ } else if (!name) {
+ errors.name = 'The name field is required.'
+ } else if (name.length < 3) {
+ errors.name = 'The name must be at least 3 characters.'
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 500)
+})
+
+app.post('/form-component/precognition-exception', (req, res) => {
+ // Return a 500 error with Precognition header to trigger exception handling
+ res.header('Precognition', 'true')
+ res.status(500).json({ message: 'Internal server error' })
+})
+
//
function renderInfiniteScroll(req, res, component, total = 40, orderByDesc = false, perPage = 15) {
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 0a3e185bb..b0e304081 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1834,6 +1834,120 @@ test.describe('Form Component', () => {
await expect(page.getByText('onError called!')).toBeVisible()
await expect(page.getByText('onFinish called!')).toBeVisible()
})
+
+ test('onBeforeValidation can block validation', async ({ page }) => {
+ await page.goto('/form-component/precognition-before-validation')
+
+ // First validate with a normal value - should work
+ await page.fill('input[name="name"]', 'John')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await expect(page.getByText('Validation blocked by onBeforeValidation')).not.toBeVisible()
+
+ // Verify data structure is correct
+ await expect(page.getByText('Data structure is correct')).toBeVisible()
+
+ // Now validate with "block" - should be blocked
+ await page.fill('input[name="name"]', 'block')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validation blocked by onBeforeValidation')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Data structure should still be correct even when blocking
+ await expect(page.getByText('Data structure is correct')).toBeVisible()
+ })
+
+ test('onBeforeValidation can be passed per validate call', async ({ page }) => {
+ await page.goto('/form-component/precognition-before-validation-per-call')
+
+ await page.fill('#name-input', 'John')
+
+ // First button uses first callback
+ await page.click('button:has-text("Validate with First")')
+
+ await expect(page.getByText('Blocked by first callback')).toBeVisible()
+ await expect(page.getByText('Blocked by second callback')).not.toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Second button uses second callback
+ await page.click('button:has-text("Validate with Second")')
+
+ await expect(page.getByText('Blocked by second callback')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ })
+
+ test('onException handles non-422 errors during validation', async ({ page }) => {
+ await page.goto('/form-component/precognition-exception')
+
+ await page.fill('#name-input', 'John')
+
+ // Trigger validation that will return 500 error
+ await page.click('button:has-text("Validate with Exception Handler")')
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Exception should be caught and displayed
+ await expect(page.getByText(/Exception caught:/)).toBeVisible()
+ })
+
+ test('sends custom headers with validation requests', async ({ page }) => {
+ await page.goto('/form-component/precognition-headers')
+
+ // Validation should succeed because custom header is sent
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should not show error because custom header was present
+ await expect(page.getByText('Custom header missing or incorrect.')).not.toBeVisible()
+ })
+
+ test('automatically cancels previous validation when new validation starts', async ({ page }) => {
+ await page.goto('/form-component/precognition-cancel')
+
+ // Start first validation (with 2 second timeout)
+ await page.fill('#name-input', 'ab')
+ await page.locator('#name-input').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+
+ // Immediately change value and trigger new validation - should cancel the first one
+ await page.fill('#name-input', 'xyz')
+ await page.locator('#name-input').blur()
+
+ // Wait for validation to complete
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show error for the second validation (xyz), not the first (ab)
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ })
+
+ test('defaults() updates validator data to prevent unnecessary requests', async ({ page }) => {
+ await page.goto('/form-component/precognition-defaults')
+
+ // Fill in a value and validate
+ await page.fill('#name-input', 'John')
+ await page.locator('#name-input').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Set this as the new defaults
+ await page.click('button:has-text("Set Defaults")')
+
+ // Blur again without changing the value - should NOT trigger validation
+ // because the validator knows the data hasn't changed since defaults
+ await page.locator('#name-input').blur()
+
+ // Should not show validating because data hasn't changed
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ })
})
test.describe('React', () => {
From d56cce64e6020b002c18d8c485c4643f7358b63a Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 7 Oct 2025 14:41:09 +0200
Subject: [PATCH 031/145] React + Svelte + refactor
---
packages/react/src/Form.ts | 3 +
.../PrecognitionBeforeValidation.tsx | 69 ++++++++++++++++++
.../PrecognitionBeforeValidationPerCall.tsx | 63 ++++++++++++++++
.../FormComponent/PrecognitionCancel.tsx | 24 +++++++
.../FormComponent/PrecognitionDefaults.tsx | 37 ++++++++++
.../FormComponent/PrecognitionException.tsx | 45 ++++++++++++
.../FormComponent/PrecognitionHeaders.tsx | 29 ++++++++
packages/svelte/src/components/Form.svelte | 3 +
.../PrecognitionBeforeValidation.svelte | 71 +++++++++++++++++++
...PrecognitionBeforeValidationPerCall.svelte | 60 ++++++++++++++++
.../FormComponent/PrecognitionCancel.svelte | 32 +++++++++
.../FormComponent/PrecognitionDefaults.svelte | 41 +++++++++++
.../PrecognitionException.svelte | 41 +++++++++++
.../FormComponent/PrecognitionHeaders.svelte | 33 +++++++++
.../FormComponent/PrecognitionDefaults.vue | 3 +-
tests/app/server.js | 6 +-
tests/form-component.spec.ts | 47 ++++++++----
17 files changed, 588 insertions(+), 19 deletions(-)
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index bdd1a2340..2921d02c1 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -215,6 +215,7 @@ const Form = forwardRef(
const defaults = () => {
defaultData.current = getFormData()
setIsDirty(false)
+ updateDataOnValidator()
}
const validate = (
@@ -247,6 +248,7 @@ const Form = forwardRef(
only: fields,
errorBag,
headers,
+ onBeforeValidation: options.onBeforeValidation,
onPrecognitionSuccess: () => {
setValidated((prev) => [...prev, ...fields])
form.clearErrors(...fields)
@@ -264,6 +266,7 @@ const Form = forwardRef(
form.setError({ ...form.errors, ...errors })
options.onError?.(errors)
},
+ onException: options.onException,
onFinish: () => {
options.onFinish?.()
},
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
new file mode 100644
index 000000000..fe78a31ab
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
@@ -0,0 +1,69 @@
+import { Form } from '@inertiajs/react'
+import { useState } from 'react'
+
+export default function PrecognitionBeforeValidation() {
+ const [blocked, setBlocked] = useState(false)
+ const [dataCorrect, setDataCorrect] = useState(false)
+
+ const handleBeforeValidation = (
+ newRequest: { data: Record; touched: string[] },
+ oldRequest: { data: Record; touched: string[] },
+ ) => {
+ // Verify the data structure is correct
+ const hasNewData = typeof newRequest.data === 'object' && newRequest.data !== null
+ const hasNewTouched = Array.isArray(newRequest.touched)
+ const hasOldData = typeof oldRequest.data === 'object' && oldRequest.data !== null
+ const hasOldTouched = Array.isArray(oldRequest.touched)
+ const hasNameField = 'name' in newRequest.data
+ const touchedContainsName = newRequest.touched.includes('name')
+
+ setDataCorrect(hasNewData && hasNewTouched && hasOldData && hasOldTouched && hasNameField && touchedContainsName)
+
+ // Block validation if name is "block"
+ if (newRequest.data.name === 'block') {
+ setBlocked(true)
+ return false
+ }
+
+ setBlocked(false)
+ return true
+ }
+
+ return (
+
+
Precognition - onBeforeValidation
+
+
+ {({ errors, invalid, validate, validating }) => (
+ <>
+
+
Name:
+
{
+ validate('name', {
+ onBeforeValidation: handleBeforeValidation,
+ })
+ }}
+ />
+ {invalid('name') &&
{errors.name}
}
+
+
+
+
Email:
+
validate('email')} />
+ {invalid('email') &&
{errors.email}
}
+
+
+ {validating && Validating...
}
+ {blocked && Validation blocked by onBeforeValidation
}
+ {dataCorrect && Data structure is correct
}
+
+ Submit
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx
new file mode 100644
index 000000000..3310817af
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx
@@ -0,0 +1,63 @@
+import { Form } from '@inertiajs/react'
+import { useState } from 'react'
+
+export default function PrecognitionBeforeValidationPerCall() {
+ const [blockedFirst, setBlockedFirst] = useState(false)
+ const [blockedSecond, setBlockedSecond] = useState(false)
+
+ const handleBeforeValidationFirst = () => {
+ setBlockedFirst(true)
+ return false
+ }
+
+ const handleBeforeValidationSecond = () => {
+ setBlockedSecond(true)
+ return false
+ }
+
+ return (
+
+
Precognition - onBeforeValidation Per Call
+
+
+ {({ validate, validating }) => (
+ <>
+ {validating && Validating...
}
+ {blockedFirst && Blocked by first callback
}
+ {blockedSecond && Blocked by second callback
}
+
+
+
+
+
+ {/* This button uses first callback */}
+
+ validate('name', {
+ onBeforeValidation: handleBeforeValidationFirst,
+ })
+ }
+ >
+ Validate with First
+
+
+ {/* This button uses second callback */}
+
+ validate('name', {
+ onBeforeValidation: handleBeforeValidationSecond,
+ })
+ }
+ >
+ Validate with Second
+
+
+ Submit
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
new file mode 100644
index 000000000..8d2b35942
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
@@ -0,0 +1,24 @@
+import { Form } from '@inertiajs/react'
+
+export default function PrecognitionCancel() {
+ return (
+
+
Precognition - Auto Cancel
+
+
+ {({ invalid, errors, validate, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
validate('name')} />
+ {invalid('name') &&
{errors.name}
}
+
+
+ Submit
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
new file mode 100644
index 000000000..a610c96ad
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
@@ -0,0 +1,37 @@
+import { Form } from '@inertiajs/react'
+import { useRef } from 'react'
+
+export default function PrecognitionDefaults() {
+ const formRef = useRef(null)
+
+ const handleSetDefaults = () => {
+ formRef.current?.defaults()
+ }
+
+ return (
+
+
Precognition - Defaults Updates Validator
+
+
+ {({ invalid, errors, validate, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
+ {invalid('name') &&
{errors.name}
}
+
+
+
+ Set Defaults
+
+ validate('name')}>
+ Validate Name
+
+ Submit
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx
new file mode 100644
index 000000000..3f9e88551
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx
@@ -0,0 +1,45 @@
+import { Form } from '@inertiajs/react'
+import { useState } from 'react'
+
+export default function PrecognitionException() {
+ const [exceptionCaught, setExceptionCaught] = useState(false)
+ const [exceptionMessage, setExceptionMessage] = useState('')
+
+ const handleException = (error: Error) => {
+ setExceptionCaught(true)
+ setExceptionMessage(error.message || 'Unknown error')
+ }
+
+ return (
+
+
Precognition - onException
+
+
+ {({ validate, validating }) => (
+ <>
+ {validating && Validating...
}
+ {exceptionCaught && Exception caught: {exceptionMessage}
}
+
+
+
+
+
+ {/* This will trigger a validation request to a non-existent endpoint */}
+
+ validate('name', {
+ onException: handleException,
+ })
+ }
+ >
+ Validate with Exception Handler
+
+
+ Submit
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx
new file mode 100644
index 000000000..931fd6231
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx
@@ -0,0 +1,29 @@
+import { Form } from '@inertiajs/react'
+
+export default function PrecognitionHeaders() {
+ return (
+
+
Precognition - Custom Headers
+
+
+ {({ invalid, errors, validate, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
validate('name')} />
+ {invalid('name') &&
{errors.name}
}
+
+
+ Submit
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 4fc149f21..f053e0b1e 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -196,6 +196,7 @@
export function defaults() {
defaultData = getFormData()
isDirty = false
+ updateDataOnValidator()
}
export function validate(
@@ -228,6 +229,7 @@
only: fields,
errorBag,
headers,
+ onBeforeValidation: options.onBeforeValidation,
onPrecognitionSuccess: () => {
validated = [...validated, ...fields]
clearErrors(...fields)
@@ -247,6 +249,7 @@
setError(mergedErrors)
options.onError?.(errors)
},
+ onException: options.onException,
onFinish: () => {
options.onFinish?.()
},
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
new file mode 100644
index 000000000..44d3c1a51
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
@@ -0,0 +1,71 @@
+
+
+
+
Precognition - onBeforeValidation
+
+
+
+
Name:
+
+ validate('name', {
+ onBeforeValidation: handleBeforeValidation,
+ })}
+ />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
+
+
+
Email:
+
validate('email')} />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+
+
+ {#if validating}
+ Validating...
+ {/if}
+ {#if blocked}
+ Validation blocked by onBeforeValidation
+ {/if}
+ {#if dataCorrect}
+ Data structure is correct
+ {/if}
+
+ Submit
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte
new file mode 100644
index 000000000..f92365489
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte
@@ -0,0 +1,60 @@
+
+
+
+
Precognition - onBeforeValidation Per Call
+
+
+ {#if validating}
+ Validating...
+ {/if}
+ {#if blockedFirst}
+ Blocked by first callback
+ {/if}
+ {#if blockedSecond}
+ Blocked by second callback
+ {/if}
+
+
+
+
+
+
+
+ validate('name', {
+ onBeforeValidation: handleBeforeValidationFirst,
+ })}
+ >
+ Validate with First
+
+
+
+
+ validate('name', {
+ onBeforeValidation: handleBeforeValidationSecond,
+ })}
+ >
+ Validate with Second
+
+
+ Submit
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
new file mode 100644
index 000000000..8adef4c73
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
@@ -0,0 +1,32 @@
+
+
+
+
Precognition - Auto Cancel
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
validate('name')} />
+ {#if invalid('name')}
+
+ {errors.name}
+
+ {/if}
+
+
+ Submit
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
new file mode 100644
index 000000000..2a2cfd3b7
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
@@ -0,0 +1,41 @@
+
+
+
+
Precognition - Defaults Updates Validator
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
+ {#if invalid('name')}
+
+ {errors.name}
+
+ {/if}
+
+
+ Set Defaults
+ validate('name')}>Validate Name
+ Submit
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte
new file mode 100644
index 000000000..ea3d17b14
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte
@@ -0,0 +1,41 @@
+
+
+
+
Precognition - onException
+
+
+ {#if validating}
+ Validating...
+ {/if}
+ {#if exceptionCaught}
+ Exception caught: {exceptionMessage}
+ {/if}
+
+
+
+
+
+
+
+ validate('name', {
+ onException: handleException,
+ })}
+ >
+ Validate with Exception Handler
+
+
+ Submit
+
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte
new file mode 100644
index 000000000..6fb67a051
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte
@@ -0,0 +1,33 @@
+
+
+
+
Precognition - Custom Headers
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
validate('name')} />
+ {#if invalid('name')}
+
+ {errors.name}
+
+ {/if}
+
+
+ Submit
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
index 8fa996cc5..205af63b7 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
@@ -23,13 +23,14 @@ const handleSetDefaults = () => {
Validating...
Set Defaults
+ Validate Name
Submit
diff --git a/tests/app/server.js b/tests/app/server.js
index de198fb91..4076ba7ef 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -1005,9 +1005,9 @@ app.post('/form-component/precognition-headers', (req, res) => {
const name = req.body['name']
const errors = {}
- // Validate that custom header is present
- if (customHeader !== 'custom-value') {
- errors.name = 'Custom header missing or incorrect.'
+ // Show error when custom header IS present (to prove it was sent)
+ if (customHeader === 'custom-value') {
+ errors.name = 'Custom header received: custom-value'
} else if (!name) {
errors.name = 'The name field is required.'
} else if (name.length < 3) {
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index b0e304081..e338213a5 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1863,20 +1863,36 @@ test.describe('Form Component', () => {
test('onBeforeValidation can be passed per validate call', async ({ page }) => {
await page.goto('/form-component/precognition-before-validation-per-call')
- await page.fill('#name-input', 'John')
+ await page.fill('#name-input', 'ab')
- // First button uses first callback
+ // Set up network listener to verify no validation requests are made
+ let validationRequestMade = false
+ page.on('request', (request) => {
+ if (request.url().includes('/form-component/precognition') && request.method() === 'POST') {
+ validationRequestMade = true
+ }
+ })
+
+ // First button uses first callback - should block validation
await page.click('button:has-text("Validate with First")')
await expect(page.getByText('Blocked by first callback')).toBeVisible()
await expect(page.getByText('Blocked by second callback')).not.toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- // Second button uses second callback
+ // Wait a moment to ensure no request was made
+ await page.waitForTimeout(300)
+ expect(validationRequestMade).toBe(false)
+
+ // Second button uses second callback - should also block validation
await page.click('button:has-text("Validate with Second")')
await expect(page.getByText('Blocked by second callback')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Wait again to ensure no request was made
+ await page.waitForTimeout(300)
+ expect(validationRequestMade).toBe(false)
})
test('onException handles non-422 errors during validation', async ({ page }) => {
@@ -1897,15 +1913,15 @@ test.describe('Form Component', () => {
test('sends custom headers with validation requests', async ({ page }) => {
await page.goto('/form-component/precognition-headers')
- // Validation should succeed because custom header is sent
+ // Fill in a valid name to trigger validation
await page.fill('input[name="name"]', 'John Doe')
await page.locator('input[name="name"]').blur()
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- // Should not show error because custom header was present
- await expect(page.getByText('Custom header missing or incorrect.')).not.toBeVisible()
+ // Should show error confirming custom header was received
+ await expect(page.getByText('Custom header received: custom-value')).toBeVisible()
})
test('automatically cancels previous validation when new validation starts', async ({ page }) => {
@@ -1928,24 +1944,25 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
})
- test('defaults() updates validator data to prevent unnecessary requests', async ({ page }) => {
+ test('defaults() updates validator data as well', async ({ page }) => {
await page.goto('/form-component/precognition-defaults')
- // Fill in a value and validate
await page.fill('#name-input', 'John')
await page.locator('#name-input').blur()
+ await page.click('button:has-text("Validate Name")')
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- // Set this as the new defaults
- await page.click('button:has-text("Set Defaults")')
-
- // Blur again without changing the value - should NOT trigger validation
- // because the validator knows the data hasn't changed since defaults
- await page.locator('#name-input').blur()
+ // Click again, should not validate again because data hasn't changed
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await page.click('button:has-text("Validate Name")')
+ await expect(page.getByText('Validating...')).not.toBeVisible()
- // Should not show validating because data hasn't changed
+ // Now change default to a different value, should not validate because value matches new default
+ await page.fill('#name-input', 'Johnny')
+ await page.click('button:has-text("Set Defaults")')
+ await page.click('button:has-text("Validate Name")')
await expect(page.getByText('Validating...')).not.toBeVisible()
})
})
From e16c2abc54d064aa4ca32dbafa9d2c235f447da6 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 7 Oct 2025 17:38:50 +0200
Subject: [PATCH 032/145] Update PrecognitionReset.vue
---
.../vue3/test-app/Pages/FormComponent/PrecognitionReset.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
index 3d225f5e8..472569b1b 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
@@ -31,7 +31,7 @@ import { Form } from '@inertiajs/vue3'
Validate All Touched
Reset All
Reset Name
- Reset Name and Email
+ Reset Name and Email
From 25774a3df7ef77cbe2fb68969d7b97445f8fb70a Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 7 Oct 2025 17:52:13 +0200
Subject: [PATCH 033/145] Force simple errors payload
---
packages/core/src/precognition.ts | 20 ++++++--
.../FormComponent/PrecognitionArrayErrors.tsx | 29 ++++++++++++
.../PrecognitionArrayErrors.svelte | 42 +++++++++++++++++
.../FormComponent/PrecognitionArrayErrors.vue | 34 ++++++++++++++
tests/app/server.js | 46 +++++++++++++++++++
tests/form-component.spec.ts | 14 ++++++
6 files changed, 180 insertions(+), 5 deletions(-)
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index eba2ca16f..f7115f55f 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -33,8 +33,10 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
const abortControllers: Record = {}
const cancelAll = () => {
- Object.values(abortControllers).forEach((controller) => controller.abort())
- Object.keys(abortControllers).forEach((key) => delete abortControllers[key])
+ Object.entries(abortControllers).forEach(([key, controller]) => {
+ controller.abort()
+ delete abortControllers[key]
+ })
}
const setTimeout = (value: number) => {
@@ -45,8 +47,16 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
}
}
- const createFingerprint = (options: PrecognitionValidateOptions) => {
- return `${options.method}:${options.url}`
+ const createFingerprint = (options: PrecognitionValidateOptions) => `${options.method}:${options.url}`
+
+ const toSimpleValidationErrors = (errors: Errors): Errors => {
+ return Object.keys(errors).reduce(
+ (carry, key) => ({
+ ...carry,
+ [key]: Array.isArray(errors[key]) ? errors[key][0] : errors[key],
+ }),
+ {},
+ )
}
const createValidateFunction = () =>
@@ -102,7 +112,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
if (error.response?.status === 422) {
const errors = error.response.data?.errors || {}
const scopedErrors = (options.errorBag ? errors[options.errorBag] || {} : errors) as Errors
- return options.onValidationError(scopedErrors)
+ return options.onValidationError(toSimpleValidationErrors(scopedErrors))
}
if (options.onException) {
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx
new file mode 100644
index 000000000..d9f35c491
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx
@@ -0,0 +1,29 @@
+import { Form } from '@inertiajs/react'
+
+export default () => {
+ return (
+
+
Form Precognition - Array Errors
+
+
+ {({ invalid, errors, validate, valid, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
validate('name')} />
+ {invalid('name') &&
{errors.name}
}
+ {valid('name') &&
Name is valid!
}
+
+
+
+
validate('email')} />
+ {invalid('email') &&
{errors.email}
}
+ {valid('email') &&
Email is valid!
}
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte
new file mode 100644
index 000000000..a36918728
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte
@@ -0,0 +1,42 @@
+
+
+
+
Form Precognition - Array Errors
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
validate('name')} />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+ {#if valid('name')}
+
Name is valid!
+ {/if}
+
+
+
+
validate('email')} />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+ {#if valid('email')}
+
Email is valid!
+ {/if}
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue
new file mode 100644
index 000000000..724829dbc
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
Form Precognition - Array Errors
+
+
+ Validating...
+
+
+
+
+ {{ errors.name }}
+
+
Name is valid!
+
+
+
+
+
+ {{ errors.email }}
+
+
Email is valid!
+
+
+
+
diff --git a/tests/app/server.js b/tests/app/server.js
index 27c634edc..ad3cac327 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -950,6 +950,52 @@ app.post('/form-component/precognition', (req, res) => {
}, 500)
})
+app.post('/form-component/precognition-array-errors', (req, res) => {
+ setTimeout(() => {
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const email = req.body['email']
+ const errors = {}
+
+ if (!name) {
+ errors.name = ['The name field is required.']
+ }
+
+ if (name && name.length < 3) {
+ errors.name = ['The name must be at least 3 characters.', 'The name contains invalid characters.']
+ }
+
+ if (!email) {
+ errors.email = ['The email field is required.']
+ }
+
+ if (email && !/\S+@\S+\.\S+/.test(email)) {
+ errors.email = ['The email must be a valid email address.', 'The email format is incorrect.']
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 500)
+})
+
+app.get('/form-component/precognition-array-errors', (req, res) =>
+ inertia.render(req, res, { component: 'FormComponent/PrecognitionArrayErrors', props: {} }),
+)
+
app.post('/form-component/precognition-files', upload.any(), (req, res) => {
setTimeout(() => {
console.log(req, req)
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index e338213a5..3b8109a69 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1443,6 +1443,20 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
})
+ test('shows only first error when server returns errors as array', async ({ page }) => {
+ await page.goto('/form-component/precognition-array-errors')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show only the first error from the array, not the second
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The name contains invalid characters.')).not.toBeVisible()
+ })
+
test('clears validation error when field becomes valid', async ({ page }) => {
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
From f502fddaa0acd34fa8e8dc7f02081541a50e06ea Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 7 Oct 2025 18:05:26 +0200
Subject: [PATCH 034/145] improve test
---
packages/core/src/types.ts | 10 +--
.../PrecognitionBeforeValidation.tsx | 26 ++------
.../PrecognitionBeforeValidation.svelte | 36 +++++------
.../PrecognitionBeforeValidation.vue | 61 ++++++++-----------
tests/form-component.spec.ts | 21 ++-----
5 files changed, 55 insertions(+), 99 deletions(-)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index aca50e76f..888419b37 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -481,17 +481,17 @@ export type FormComponentProps = Partial<
validateTimeout?: number
}
-type OnBeforeValidationCallback = (
- newRequest: { data: RequestData; touched: string[] },
- oldRequest: { data: RequestData; touched: string[] },
-) => boolean | undefined
+type RevalidatePayload = {
+ data: RequestData
+ touched: string[]
+}
export type FormComponentValidateOptions = {
only?: string | string[]
onSuccess?: () => void
onError?: (errors: Errors) => void
onFinish?: () => void
- onBeforeValidation?: OnBeforeValidationCallback
+ onBeforeValidation?: (newRequest: RevalidatePayload, oldRequest: RevalidatePayload) => boolean | undefined
onException?: (error: Error) => void
}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
index fe78a31ab..778b7de4d 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
@@ -1,31 +1,19 @@
import { Form } from '@inertiajs/react'
-import { useState } from 'react'
+import { isEqual } from 'lodash-es'
export default function PrecognitionBeforeValidation() {
- const [blocked, setBlocked] = useState(false)
- const [dataCorrect, setDataCorrect] = useState(false)
-
const handleBeforeValidation = (
newRequest: { data: Record; touched: string[] },
oldRequest: { data: Record; touched: string[] },
) => {
- // Verify the data structure is correct
- const hasNewData = typeof newRequest.data === 'object' && newRequest.data !== null
- const hasNewTouched = Array.isArray(newRequest.touched)
- const hasOldData = typeof oldRequest.data === 'object' && oldRequest.data !== null
- const hasOldTouched = Array.isArray(oldRequest.touched)
- const hasNameField = 'name' in newRequest.data
- const touchedContainsName = newRequest.touched.includes('name')
-
- setDataCorrect(hasNewData && hasNewTouched && hasOldData && hasOldTouched && hasNameField && touchedContainsName)
+ const payloadIsCorrect =
+ isEqual(newRequest, { data: { name: 'block' }, touched: ['name'] }) &&
+ isEqual(oldRequest, { data: {}, touched: [] })
- // Block validation if name is "block"
- if (newRequest.data.name === 'block') {
- setBlocked(true)
+ if (payloadIsCorrect && newRequest.data.name === 'block') {
return false
}
- setBlocked(false)
return true
}
@@ -33,7 +21,7 @@ export default function PrecognitionBeforeValidation() {
Precognition - onBeforeValidation
-
+
{({ errors, invalid, validate, validating }) => (
<>
@@ -57,8 +45,6 @@ export default function PrecognitionBeforeValidation() {
{validating && Validating...
}
- {blocked && Validation blocked by onBeforeValidation
}
- {dataCorrect && Data structure is correct
}
Submit
>
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
index 44d3c1a51..39544007e 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
@@ -1,30 +1,20 @@
@@ -32,7 +22,15 @@
Precognition - onBeforeValidation
-
+
Name:
Validating...
{/if}
- {#if blocked}
-
Validation blocked by onBeforeValidation
- {/if}
- {#if dataCorrect}
-
Data structure is correct
- {/if}
Submit
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
index 2505d97e9..5a25b9f51 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
@@ -1,8 +1,33 @@
+
+
Precognition - onBeforeValidation
-
+
Name:
Validating...
-
Validation blocked by onBeforeValidation
-
Data structure is correct
Submit
-
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 3b8109a69..708b767df 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1852,26 +1852,13 @@ test.describe('Form Component', () => {
test('onBeforeValidation can block validation', async ({ page }) => {
await page.goto('/form-component/precognition-before-validation')
- // First validate with a normal value - should work
- await page.fill('input[name="name"]', 'John')
- await page.locator('input[name="name"]').blur()
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
- await expect(page.getByText('Validation blocked by onBeforeValidation')).not.toBeVisible()
-
- // Verify data structure is correct
- await expect(page.getByText('Data structure is correct')).toBeVisible()
-
- // Now validate with "block" - should be blocked
await page.fill('input[name="name"]', 'block')
await page.locator('input[name="name"]').blur()
- await expect(page.getByText('Validation blocked by onBeforeValidation')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Data structure should still be correct even when blocking
- await expect(page.getByText('Data structure is correct')).toBeVisible()
+ for (let i = 0; i < 5; i++) {
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await page.waitForTimeout(50)
+ }
})
test('onBeforeValidation can be passed per validate call', async ({ page }) => {
From 0587acbb56495ed2bc41c6ab48f0b789b919b5ef Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Wed, 8 Oct 2025 17:33:45 +0200
Subject: [PATCH 035/145] `touched()` method
---
packages/core/src/types.ts | 1 +
packages/react/src/Form.ts | 9 ++++
.../Pages/FormComponent/PrecognitionTouch.tsx | 6 ++-
packages/svelte/src/components/Form.svelte | 10 +++++
.../FormComponent/PrecognitionTouch.svelte | 5 +++
packages/vue3/src/form.ts | 9 ++++
.../Pages/FormComponent/PrecognitionTouch.vue | 6 ++-
tests/form-component.spec.ts | 43 +++++++++++++++++++
8 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 888419b37..b9724486c 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -510,6 +510,7 @@ export type FormComponentMethods = {
maybeOptions?: FormComponentValidateOptions,
) => void
touch: (field: string | string[]) => void
+ touched(field?: string): boolean
}
export type FormComponentonSubmitCompleteArguments = Pick
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 2921d02c1..6ac5ca2bb 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -280,6 +280,14 @@ const Form = forwardRef(
setTouched((prev) => [...new Set([...prev, ...fields])])
}
+ const isTouched = (field?: string): boolean => {
+ if (typeof field === 'string') {
+ return touched.includes(field)
+ }
+
+ return touched.length > 0
+ }
+
const exposed = () => ({
errors: form.errors,
hasErrors: form.hasErrors,
@@ -301,6 +309,7 @@ const Form = forwardRef(
invalid: (field: string) => form.errors[field] !== undefined,
validate,
touch,
+ touched: isTouched,
setValidationTimeout: (duration: number) => validator.setTimeout(duration),
validateFiles: () => validator.validateFiles(true),
})
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
index 2122b7b0c..9acfbde39 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
@@ -6,7 +6,7 @@ export default () => {
Form Precognition Touch
- {({ invalid, errors, validate, touch, validating }) => (
+ {({ invalid, errors, validate, touch, touched, validating }) => (
<>
{validating && Validating...
}
@@ -20,6 +20,10 @@ export default () => {
{invalid('email') && {errors.email}
}
+ {touched('name') ? 'Name is touched' : 'Name is not touched'}
+ {touched('email') ? 'Email is touched' : 'Email is not touched'}
+ {touched() ? 'Form has touched fields' : 'Form has no touched fields'}
+
validate()}>
Validate All Touched
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index f053e0b1e..d1adba604 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -263,6 +263,14 @@
touched = [...new Set([...touched, ...fields])]
}
+ export function isTouched(field?: string): boolean {
+ if (typeof field === 'string') {
+ return touched.includes(field)
+ }
+
+ return touched.length > 0
+ }
+
export function valid(field: string): boolean {
return validated.includes(field) && $form.errors[field] === undefined
}
@@ -302,6 +310,7 @@
// Create reactive slot props that update when state changes
$: slotValid = (field: string) => validated.includes(field) && slotErrors[field] === undefined
$: slotInvalid = (field: string) => slotErrors[field] !== undefined
+ $: slotTouched = (field?: string) => (typeof field === 'string' ? touched.includes(field) : touched.length > 0)
{#if validating}
@@ -33,6 +34,10 @@
{/if}
+ {touched('name') ? 'Name is touched' : 'Name is not touched'}
+ {touched('email') ? 'Email is touched' : 'Email is not touched'}
+ {touched() ? 'Form has touched fields' : 'Form has no touched fields'}
+
validate()}>Validate All Touched
touch(['name', 'email'])}>Touch Name and Email
{
+ if (typeof field === 'string') {
+ return touched.value.includes(field)
+ }
+
+ return touched.value.length > 0
+ }
+
const exposed = {
get errors() {
return form.errors
@@ -365,6 +373,7 @@ const Form = defineComponent({
invalid: (field: string) => form.errors[field] !== undefined,
validate,
touch,
+ touched: isTouched,
setValidationTimeout: (duration: number) => validator.setTimeout(duration),
validateFiles: () => validator.validateFiles(true),
}
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
index dbe20d6cf..dd77870af 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
@@ -10,7 +10,7 @@ import { Form } from '@inertiajs/vue3'
action="/form-component/precognition"
method="post"
:validate-timeout="100"
- #default="{ invalid, errors, validate, touch, validating }"
+ #default="{ invalid, errors, validate, touch, touched, validating }"
>
Validating...
@@ -28,6 +28,10 @@ import { Form } from '@inertiajs/vue3'
+ {{ touched('name') ? 'Name is touched' : 'Name is not touched' }}
+ {{ touched('email') ? 'Email is touched' : 'Email is not touched' }}
+ {{ touched() ? 'Form has touched fields' : 'Form has no touched fields' }}
+
Validate All Touched
Touch Name and Email
{
await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
})
+ test('touched() returns false when no fields are touched', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await expect(page.getByTestId('any-touched')).toHaveText('Form has no touched fields')
+ await expect(page.getByTestId('name-touched')).toHaveText('Name is not touched')
+ await expect(page.getByTestId('email-touched')).toHaveText('Email is not touched')
+ })
+
+ test('touched(field) returns true when specific field is touched', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.locator('input[name="name"]').focus()
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByTestId('name-touched')).toHaveText('Name is touched')
+ await expect(page.getByTestId('email-touched')).toHaveText('Email is not touched')
+ await expect(page.getByTestId('any-touched')).toHaveText('Form has touched fields')
+ })
+
+ test('touched() returns true when any field is touched', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.locator('input[name="email"]').focus()
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByTestId('any-touched')).toHaveText('Form has touched fields')
+ await expect(page.getByTestId('email-touched')).toHaveText('Email is touched')
+ await expect(page.getByTestId('name-touched')).toHaveText('Name is not touched')
+ })
+
+ test('touched() updates when multiple fields are touched', async ({ page }) => {
+ await page.goto('/form-component/precognition-touch')
+
+ await page.locator('input[name="name"]').focus()
+ await page.locator('input[name="name"]').blur()
+ await page.locator('input[name="email"]').focus()
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByTestId('name-touched')).toHaveText('Name is touched')
+ await expect(page.getByTestId('email-touched')).toHaveText('Email is touched')
+ await expect(page.getByTestId('any-touched')).toHaveText('Form has touched fields')
+ })
+
test('validate with specific field works independently of touched state', async ({ page }) => {
await page.goto('/form-component/precognition-validate')
From cd16aba1942d838615dd1bfd0db3bb83ee113368 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Wed, 8 Oct 2025 17:42:11 +0200
Subject: [PATCH 036/145] Prop for errors in array format
---
packages/core/src/precognition.ts | 5 +-
packages/core/src/types.ts | 1 +
packages/react/src/Form.ts | 2 +
.../FormComponent/PrecognitionAllErrors.tsx | 58 ++++++++++++++++++
packages/svelte/src/components/Form.svelte | 2 +
.../PrecognitionAllErrors.svelte | 59 +++++++++++++++++++
packages/vue3/src/form.ts | 5 ++
.../FormComponent/PrecognitionAllErrors.vue | 45 ++++++++++++++
tests/form-component.spec.ts | 28 +++++++++
9 files changed, 204 insertions(+), 1 deletion(-)
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index f7115f55f..fdb9a267d 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -15,6 +15,7 @@ type PrecognitionValidateOptions = Pick, 'method' | 'data' |
url: string
onPrecognitionSuccess: () => void
onValidationError: (errors: Errors) => void
+ simpleValidationErrors?: boolean
}
interface PrecognitionValidator {
@@ -112,7 +113,9 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
if (error.response?.status === 422) {
const errors = error.response.data?.errors || {}
const scopedErrors = (options.errorBag ? errors[options.errorBag] || {} : errors) as Errors
- return options.onValidationError(toSimpleValidationErrors(scopedErrors))
+ const formattedErrors =
+ options.simpleValidationErrors === false ? scopedErrors : toSimpleValidationErrors(scopedErrors)
+ return options.onValidationError(formattedErrors)
}
if (options.onException) {
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index b9724486c..dc975b48e 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -479,6 +479,7 @@ export type FormComponentProps = Partial<
setDefaultsOnSuccess?: boolean
validateFiles?: boolean
validateTimeout?: number
+ simpleValidationErrors?: boolean
}
type RevalidatePayload = {
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 6ac5ca2bb..89d01f364 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -68,6 +68,7 @@ const Form = forwardRef(
invalidateCacheTags = [],
validateFiles = false,
validateTimeout = 1500,
+ simpleValidationErrors = true,
children,
...props
},
@@ -248,6 +249,7 @@ const Form = forwardRef(
only: fields,
errorBag,
headers,
+ simpleValidationErrors,
onBeforeValidation: options.onBeforeValidation,
onPrecognitionSuccess: () => {
setValidated((prev) => [...prev, ...fields])
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx
new file mode 100644
index 000000000..a0be71ede
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx
@@ -0,0 +1,58 @@
+import { Form } from '@inertiajs/react'
+
+export default () => {
+ return (
+
+
Form Precognition - All Errors
+
+
+ {({ invalid, errors, validate, valid, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+
validate('name')} />
+ {invalid('name') && (
+
+ {Array.isArray(errors.name) ? (
+ errors.name.map((error, index) => (
+
+ {error}
+
+ ))
+ ) : (
+
{errors.name}
+ )}
+
+ )}
+ {valid('name') &&
Name is valid!
}
+
+
+
+
validate('email')} />
+ {invalid('email') && (
+
+ {Array.isArray(errors.email) ? (
+ errors.email.map((error, index) => (
+
+ {error}
+
+ ))
+ ) : (
+
{errors.email}
+ )}
+
+ )}
+ {valid('email') &&
Email is valid!
}
+
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index d1adba604..ddcc09a72 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -41,6 +41,7 @@
export let setDefaultsOnSuccess: FormComponentProps['setDefaultsOnSuccess'] = false
export let validateFiles: FormComponentProps['validateFiles'] = false
export let validateTimeout: FormComponentProps['validateTimeout'] = 1500
+ export let simpleValidationErrors: FormComponentProps['simpleValidationErrors'] = true
type FormSubmitOptions = Omit
@@ -229,6 +230,7 @@
only: fields,
errorBag,
headers,
+ simpleValidationErrors,
onBeforeValidation: options.onBeforeValidation,
onPrecognitionSuccess: () => {
validated = [...validated, ...fields]
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte
new file mode 100644
index 000000000..647b3a955
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte
@@ -0,0 +1,59 @@
+
+
+
+
Form Precognition - All Errors
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+
validate('name')} />
+ {#if invalid('name')}
+
+ {#if Array.isArray(errors.name)}
+ {#each errors.name as error, index}
+
{error}
+ {/each}
+ {:else}
+
{errors.name}
+ {/if}
+
+ {/if}
+ {#if valid('name')}
+
Name is valid!
+ {/if}
+
+
+
+
validate('email')} />
+ {#if invalid('email')}
+
+ {#if Array.isArray(errors.email)}
+ {#each errors.email as error, index}
+
{error}
+ {/each}
+ {:else}
+
{errors.email}
+ {/if}
+
+ {/if}
+ {#if valid('email')}
+
Email is valid!
+ {/if}
+
+
+
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 179fe6cf4..697e58706 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -122,6 +122,10 @@ const Form = defineComponent({
type: Number as PropType,
default: 1500,
},
+ simpleValidationErrors: {
+ type: Boolean as PropType,
+ default: true,
+ },
},
setup(props, { slots, attrs, expose }) {
const form = useForm>({})
@@ -295,6 +299,7 @@ const Form = defineComponent({
only: fields,
errorBag: props.errorBag,
headers: props.headers,
+ simpleValidationErrors: props.simpleValidationErrors,
onBeforeValidation: options.onBeforeValidation,
onPrecognitionSuccess: () => {
validated.value = [...validated.value, ...fields]
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue
new file mode 100644
index 000000000..b88737c29
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
Form Precognition - All Errors
+
+
+ Validating...
+
+
+
+
+
+
+ {{ error }}
+
+
+
{{ errors.name }}
+
+
Name is valid!
+
+
+
+
+
+
+
+ {{ error }}
+
+
+
{{ errors.email }}
+
+
Email is valid!
+
+
+
+
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index c407c93b7..a346b291f 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1457,6 +1457,34 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name contains invalid characters.')).not.toBeVisible()
})
+ test('shows all errors when simpleValidationErrors is false', async ({ page }) => {
+ await page.goto('/form-component/precognition-all-errors')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show all errors from the array
+ await expect(page.getByTestId('name-error-0')).toHaveText('The name must be at least 3 characters.')
+ await expect(page.getByTestId('name-error-1')).toHaveText('The name contains invalid characters.')
+ })
+
+ test('shows all email errors when simpleValidationErrors is false', async ({ page }) => {
+ await page.goto('/form-component/precognition-all-errors')
+
+ await page.fill('input[name="email"]', 'invalid')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show all errors from the array
+ await expect(page.getByTestId('email-error-0')).toHaveText('The email must be a valid email address.')
+ await expect(page.getByTestId('email-error-1')).toHaveText('The email format is incorrect.')
+ })
+
test('clears validation error when field becomes valid', async ({ page }) => {
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
From 8c34a380d8357657a03e7cc6cef7ed6835ce3880 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Wed, 8 Oct 2025 17:47:12 +0200
Subject: [PATCH 037/145] Update Form.svelte
---
packages/svelte/src/components/Form.svelte | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index ddcc09a72..366dab022 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -52,7 +52,7 @@
let validating = false
let validated: string[] = []
- let touched: string[] = []
+ let touchedFields: string[] = []
const validator = usePrecognition({
onStart: () => {
@@ -168,9 +168,9 @@
updateDataOnValidator()
if (fields.length === 0) {
- touched = []
+ touchedFields = []
} else {
- touched = touched.filter((field) => !fields.includes(field))
+ touchedFields = touchedFields.filter((field) => !fields.includes(field))
}
}
@@ -210,11 +210,11 @@
if (typeof only === 'object' && !Array.isArray(only)) {
// Called as validate({ only: [...], onSuccess, onError, onFinish })
const onlyFields = only.only
- fields = onlyFields === undefined ? touched : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
+ fields = onlyFields === undefined ? touchedFields : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
options = only
} else {
// Called as validate('field') or validate(['field1', 'field2']) or validate('field', {options})
- fields = only === undefined ? touched : Array.isArray(only) ? only : [only]
+ fields = only === undefined ? touchedFields : Array.isArray(only) ? only : [only]
options = maybeOptions || {}
}
@@ -262,15 +262,15 @@
const fields = Array.isArray(field) ? field : [field]
// Use Set to avoid duplicates
- touched = [...new Set([...touched, ...fields])]
+ touchedFields = [...new Set([...touchedFields, ...fields])]
}
- export function isTouched(field?: string): boolean {
+ export function touched(field?: string): boolean {
if (typeof field === 'string') {
- return touched.includes(field)
+ return touchedFields.includes(field)
}
- return touched.length > 0
+ return touchedFields.length > 0
}
export function valid(field: string): boolean {
@@ -312,7 +312,8 @@
// Create reactive slot props that update when state changes
$: slotValid = (field: string) => validated.includes(field) && slotErrors[field] === undefined
$: slotInvalid = (field: string) => slotErrors[field] !== undefined
- $: slotTouched = (field?: string) => (typeof field === 'string' ? touched.includes(field) : touched.length > 0)
+ $: slotTouched = (field?: string) =>
+ typeof field === 'string' ? touchedFields.includes(field) : touchedFields.length > 0
Date: Thu, 9 Oct 2025 09:49:47 +0200
Subject: [PATCH 038/145] Update server.js
---
tests/app/server.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/app/server.js b/tests/app/server.js
index ad3cac327..9d3c21fc9 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -1094,9 +1094,9 @@ app.post('/form-component/precognition-headers', (req, res) => {
})
app.post('/form-component/precognition-exception', (req, res) => {
- // Return a 500 error with Precognition header to trigger exception handling
- res.header('Precognition', 'true')
- res.status(500).json({ message: 'Internal server error' })
+ setTimeout(() => {
+ res.status(500).json({ message: 'Internal server error' })
+ }, 500)
})
//
From c7345188d693fdb407b4868b47fc99a324ce39fc Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 9 Oct 2025 10:18:13 +0200
Subject: [PATCH 039/145] Improve cancel test
---
.../FormComponent/PrecognitionCancel.tsx | 2 +-
.../FormComponent/PrecognitionCancel.svelte | 4 +-
.../FormComponent/PrecognitionCancel.vue | 4 +-
tests/app/server.js | 80 +++++++++----------
4 files changed, 45 insertions(+), 45 deletions(-)
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
index 8d2b35942..3ed805956 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
@@ -5,7 +5,7 @@ export default function PrecognitionCancel() {
Precognition - Auto Cancel
-
+
{({ invalid, errors, validate, validating }) => (
<>
{validating && Validating...
}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
index 8adef4c73..046a3b3c5 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
@@ -6,9 +6,9 @@
Precognition - Auto Cancel
Precognition - Auto Cancel
Validating...
diff --git a/tests/app/server.js b/tests/app/server.js
index 9d3c21fc9..039c5a968 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -12,6 +12,7 @@ app.use(bodyParser.json({ extended: true }))
const upload = multer()
const adapters = ['react', 'svelte', 'vue3']
+const runsInCI = !!process.env.CI
if (!adapters.includes(inertia.package)) {
throw new Error(`Invalid adapter package "${inertia.package}". Expected one of: ${adapters.join(', ')}.`)
@@ -909,45 +910,48 @@ app.get('/form-component/invalidate-tags/:propType', (req, res) =>
//
app.post('/form-component/precognition', (req, res) => {
- setTimeout(() => {
- const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
- const name = req.body['name']
- const email = req.body['email']
- const errors = {}
-
- if (!name) {
- errors.name = 'The name field is required.'
- }
-
- if (name && name.length < 3) {
- errors.name = 'The name must be at least 3 characters.'
- }
-
- if (!email) {
- errors.email = 'The email field is required.'
- }
-
- if (email && !/\S+@\S+\.\S+/.test(email)) {
- errors.email = 'The email must be a valid email address.'
- }
-
- if (only.length) {
- Object.keys(errors).forEach((key) => {
- if (!only.includes(key)) {
- delete errors[key]
- }
- })
- }
+ setTimeout(
+ () => {
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const email = req.body['email']
+ const errors = {}
+
+ if (!name) {
+ errors.name = 'The name field is required.'
+ }
+
+ if (name && name.length < 3) {
+ errors.name = 'The name must be at least 3 characters.'
+ }
+
+ if (!email) {
+ errors.email = 'The email field is required.'
+ }
+
+ if (email && !/\S+@\S+\.\S+/.test(email)) {
+ errors.email = 'The email must be a valid email address.'
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
- res.header('Precognition', 'true')
- res.header('Vary', 'Precognition')
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
- if (Object.keys(errors).length) {
- return res.status(422).json({ errors })
- }
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
- return res.status(204).header('Precognition-Success', 'true').send()
- }, 500)
+ return res.status(204).header('Precognition-Success', 'true').send()
+ },
+ !!req.query['slow'] ? 2000 : 500,
+ )
})
app.post('/form-component/precognition-array-errors', (req, res) => {
@@ -992,10 +996,6 @@ app.post('/form-component/precognition-array-errors', (req, res) => {
}, 500)
})
-app.get('/form-component/precognition-array-errors', (req, res) =>
- inertia.render(req, res, { component: 'FormComponent/PrecognitionArrayErrors', props: {} }),
-)
-
app.post('/form-component/precognition-files', upload.any(), (req, res) => {
setTimeout(() => {
console.log(req, req)
From 5d40fa58913bb35e39201c35c5d17bbec20724f1 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 9 Oct 2025 10:18:26 +0200
Subject: [PATCH 040/145] wip
---
tests/form-component.spec.ts | 18 +++++++++++-------
tests/support.ts | 14 +++++++++++++-
2 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index a346b291f..45753fdd9 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1999,21 +1999,25 @@ test.describe('Form Component', () => {
test('automatically cancels previous validation when new validation starts', async ({ page }) => {
await page.goto('/form-component/precognition-cancel')
- // Start first validation (with 2 second timeout)
+ requests.listenForFailed(page)
+ requests.listenForResponses(page)
+
await page.fill('#name-input', 'ab')
await page.locator('#name-input').blur()
-
await expect(page.getByText('Validating...')).toBeVisible()
// Immediately change value and trigger new validation - should cancel the first one
- await page.fill('#name-input', 'xyz')
+ await page.fill('#name-input', 'xy')
await page.locator('#name-input').blur()
-
- // Wait for validation to complete
await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Should show error for the second validation (xyz), not the first (ab)
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
+ // One cancelled, one 422 response
+ expect(requests.failed).toHaveLength(1)
+ expect(requests.responses).toHaveLength(1)
+
+ const cancelledRequestError = await requests.failed[0].failure()?.errorText
+ expect(cancelledRequestError).toBe('net::ERR_ABORTED')
})
test('defaults() updates validator data as well', async ({ page }) => {
diff --git a/tests/support.ts b/tests/support.ts
index bae7d4abb..a3d2e0009 100644
--- a/tests/support.ts
+++ b/tests/support.ts
@@ -1,4 +1,4 @@
-import { expect, Page, Request } from '@playwright/test'
+import { expect, Page, Request, Response } from '@playwright/test'
export const clickAndWaitForResponse = async (
page: Page,
@@ -43,6 +43,8 @@ export const consoleMessages = {
export const requests = {
requests: [] as Request[],
finished: [] as Request[],
+ failed: [] as Request[],
+ responses: [] as Response[],
listen(page: Page) {
this.requests = []
@@ -53,6 +55,16 @@ export const requests = {
this.finished = []
page.on('requestfinished', (request) => this.finished.push(request))
},
+
+ listenForFailed(page: Page) {
+ this.failed = []
+ page.on('requestfailed', (request) => this.failed.push(request))
+ },
+
+ listenForResponses(page: Page) {
+ this.responses = []
+ page.on('response', (data) => this.responses.push(data))
+ },
}
export const shouldBeDumpPage = async (page: Page, method: 'get' | 'post' | 'patch' | 'put' | 'delete') => {
From 161bc5b6d6ed115c1283003352e780e78a675e8e Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 9 Oct 2025 11:24:58 +0200
Subject: [PATCH 041/145] manual cancel
---
packages/core/src/precognition.ts | 2 ++
packages/core/src/types.ts | 1 +
packages/react/src/Form.ts | 4 +++
.../PrecognitionManualCancel.tsx | 26 ++++++++++++++++++
packages/svelte/src/components/Form.svelte | 6 +++++
.../PrecognitionManualCancel.svelte | 27 +++++++++++++++++++
packages/vue3/src/form.ts | 4 +++
.../PrecognitionManualCancel.vue | 25 +++++++++++++++++
tests/form-component.spec.ts | 18 +++++++++++++
9 files changed, 113 insertions(+)
create mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx
create mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte
create mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index fdb9a267d..c12a8af88 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -23,6 +23,7 @@ interface PrecognitionValidator {
validateFiles: (value: boolean) => void
validate: (options: PrecognitionValidateOptions) => void
setTimeout: (value: number) => void
+ cancelAll: () => void
}
export default function usePrecognition(precognitionOptions: UsePrecognitionOptions): PrecognitionValidator {
@@ -148,5 +149,6 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
},
setTimeout,
validate,
+ cancelAll,
}
}
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index dc975b48e..aad328027 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -512,6 +512,7 @@ export type FormComponentMethods = {
) => void
touch: (field: string | string[]) => void
touched(field?: string): boolean
+ cancelValidation: () => void
}
export type FormComponentonSubmitCompleteArguments = Pick
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 89d01f364..fef96e720 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -314,6 +314,10 @@ const Form = forwardRef(
touched: isTouched,
setValidationTimeout: (duration: number) => validator.setTimeout(duration),
validateFiles: () => validator.validateFiles(true),
+ cancelValidation: () => {
+ validator.cancelAll()
+ setValidating(false)
+ },
})
useImperativeHandle(ref, exposed, [
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx
new file mode 100644
index 000000000..5624a7e80
--- /dev/null
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx
@@ -0,0 +1,26 @@
+import { Form } from '@inertiajs/react'
+
+export default function PrecognitionManualCancel() {
+ return (
+
+
Precognition - Manual Cancel
+
+
+ {({ validate, cancelValidation, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+ validate('name')} />
+
+
+ cancelValidation()}>
+ Cancel Validation
+
+ Submit
+ >
+ )}
+
+
+ )
+}
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 366dab022..8038889ce 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -289,6 +289,11 @@
validator.validateFiles(true)
}
+ export function cancelValidation() {
+ validator.cancelAll()
+ validating = false
+ }
+
onMount(() => {
defaultData = getFormData()
@@ -347,5 +352,6 @@
invalid={slotInvalid}
{setValidationTimeout}
{validateFilesEnabled}
+ {cancelValidation}
/>
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte
new file mode 100644
index 000000000..2cfe7ca3c
--- /dev/null
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte
@@ -0,0 +1,27 @@
+
+
+
+
Precognition - Manual Cancel
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+ validate('name')} />
+
+
+ cancelValidation()}>Cancel Validation
+ Submit
+
+
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 697e58706..d28376d25 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -381,6 +381,10 @@ const Form = defineComponent({
touched: isTouched,
setValidationTimeout: (duration: number) => validator.setTimeout(duration),
validateFiles: () => validator.validateFiles(true),
+ cancelValidation: () => {
+ validator.cancelAll()
+ validating.value = false
+ },
}
expose
(exposed)
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue
new file mode 100644
index 000000000..0a07e6099
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
Precognition - Manual Cancel
+
+
+ Validating...
+
+
+
+
+
+ Cancel Validation
+ Submit
+
+
+
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 45753fdd9..8793c86d7 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -2020,6 +2020,24 @@ test.describe('Form Component', () => {
expect(cancelledRequestError).toBe('net::ERR_ABORTED')
})
+ test('cancelValidation() cancels in-flight validation and resets validating state', async ({ page }) => {
+ await page.goto('/form-component/precognition-manual-cancel')
+
+ requests.listenForFailed(page)
+
+ await page.fill('#name-input', 'ab')
+ await page.locator('#name-input').blur()
+ await expect(page.getByText('Validating...')).toBeVisible()
+
+ await page.getByText('Cancel Validation').click()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await page.waitForTimeout(100)
+ expect(requests.failed).toHaveLength(1)
+
+ const cancelledRequestError = await requests.failed[0].failure()?.errorText
+ expect(cancelledRequestError).toBe('net::ERR_ABORTED')
+ })
+
test('defaults() updates validator data as well', async ({ page }) => {
await page.goto('/form-component/precognition-defaults')
From 5ed51bb578d3919b1a15ae32fd3c3fe3d1e9ba96 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 9 Oct 2025 13:48:10 +0200
Subject: [PATCH 042/145] Remove redundant test
---
.../PrecognitionBeforeValidationPerCall.tsx | 63 -------------------
...PrecognitionBeforeValidationPerCall.svelte | 60 ------------------
.../PrecognitionBeforeValidationPerCall.vue | 59 -----------------
tests/form-component.spec.ts | 35 -----------
4 files changed, 217 deletions(-)
delete mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx
delete mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte
delete mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx
deleted file mode 100644
index 3310817af..000000000
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Form } from '@inertiajs/react'
-import { useState } from 'react'
-
-export default function PrecognitionBeforeValidationPerCall() {
- const [blockedFirst, setBlockedFirst] = useState(false)
- const [blockedSecond, setBlockedSecond] = useState(false)
-
- const handleBeforeValidationFirst = () => {
- setBlockedFirst(true)
- return false
- }
-
- const handleBeforeValidationSecond = () => {
- setBlockedSecond(true)
- return false
- }
-
- return (
-
-
Precognition - onBeforeValidation Per Call
-
-
- {({ validate, validating }) => (
- <>
- {validating && Validating...
}
- {blockedFirst && Blocked by first callback
}
- {blockedSecond && Blocked by second callback
}
-
-
-
-
-
- {/* This button uses first callback */}
-
- validate('name', {
- onBeforeValidation: handleBeforeValidationFirst,
- })
- }
- >
- Validate with First
-
-
- {/* This button uses second callback */}
-
- validate('name', {
- onBeforeValidation: handleBeforeValidationSecond,
- })
- }
- >
- Validate with Second
-
-
- Submit
- >
- )}
-
-
- )
-}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte
deleted file mode 100644
index f92365489..000000000
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.svelte
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
Precognition - onBeforeValidation Per Call
-
-
- {#if validating}
- Validating...
- {/if}
- {#if blockedFirst}
- Blocked by first callback
- {/if}
- {#if blockedSecond}
- Blocked by second callback
- {/if}
-
-
-
-
-
-
-
- validate('name', {
- onBeforeValidation: handleBeforeValidationFirst,
- })}
- >
- Validate with First
-
-
-
-
- validate('name', {
- onBeforeValidation: handleBeforeValidationSecond,
- })}
- >
- Validate with Second
-
-
- Submit
-
-
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue
deleted file mode 100644
index d5d5fe11a..000000000
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidationPerCall.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
Precognition - onBeforeValidation Per Call
-
-
- Validating...
- Blocked by first callback
- Blocked by second callback
-
-
-
-
-
-
-
- Validate with First
-
-
-
-
- Validate with Second
-
-
- Submit
-
-
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 8793c86d7..ea5ecda35 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1932,41 +1932,6 @@ test.describe('Form Component', () => {
}
})
- test('onBeforeValidation can be passed per validate call', async ({ page }) => {
- await page.goto('/form-component/precognition-before-validation-per-call')
-
- await page.fill('#name-input', 'ab')
-
- // Set up network listener to verify no validation requests are made
- let validationRequestMade = false
- page.on('request', (request) => {
- if (request.url().includes('/form-component/precognition') && request.method() === 'POST') {
- validationRequestMade = true
- }
- })
-
- // First button uses first callback - should block validation
- await page.click('button:has-text("Validate with First")')
-
- await expect(page.getByText('Blocked by first callback')).toBeVisible()
- await expect(page.getByText('Blocked by second callback')).not.toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Wait a moment to ensure no request was made
- await page.waitForTimeout(300)
- expect(validationRequestMade).toBe(false)
-
- // Second button uses second callback - should also block validation
- await page.click('button:has-text("Validate with Second")')
-
- await expect(page.getByText('Blocked by second callback')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Wait again to ensure no request was made
- await page.waitForTimeout(300)
- expect(validationRequestMade).toBe(false)
- })
-
test('onException handles non-422 errors during validation', async ({ page }) => {
await page.goto('/form-component/precognition-exception')
From d105ede1208f2698ce882b983883a03c05691e78 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 9 Oct 2025 14:22:07 +0200
Subject: [PATCH 043/145] Unify tests
---
.../FormComponent/PrecognitionCallbacks.tsx | 40 ++++++++++++++++-
.../FormComponent/PrecognitionException.tsx | 45 -------------------
.../PrecognitionCallbacks.svelte | 39 +++++++++++++++-
.../PrecognitionException.svelte | 41 -----------------
.../FormComponent/PrecognitionCallbacks.vue | 36 ++++++++++++++-
.../FormComponent/PrecognitionException.vue | 41 -----------------
tests/form-component.spec.ts | 2 +-
7 files changed, 113 insertions(+), 131 deletions(-)
delete mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx
delete mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte
delete mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
index bc418536b..ba6c70af4 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
@@ -5,11 +5,19 @@ export default () => {
const [successCalled, setSuccessCalled] = useState(false)
const [errorCalled, setErrorCalled] = useState(false)
const [finishCalled, setFinishCalled] = useState(false)
+ const [exceptionCaught, setExceptionCaught] = useState(false)
+ const [exceptionMessage, setExceptionMessage] = useState('')
+
+ const handleException = (error: Error) => {
+ setExceptionCaught(true)
+ setExceptionMessage(error.message || 'Unknown error')
+ }
return (
-
Form Precognition Callbacks
+
Form Precognition Callbacks & Exceptions
+
Callbacks Test
{({ validate, validating, touch }) => (
<>
@@ -58,6 +66,36 @@ export default () => {
>
)}
+
+
+
+
Exception Test
+
+ {({ validate, validating }) => (
+ <>
+ {validating && Validating...
}
+ {exceptionCaught && Exception caught: {exceptionMessage}
}
+
+
+
+
+
+ {/* This will trigger a validation request to a non-existent endpoint */}
+
+ validate('name', {
+ onException: handleException,
+ })
+ }
+ >
+ Validate with Exception Handler
+
+
+ Submit
+ >
+ )}
+
)
}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx
deleted file mode 100644
index 3f9e88551..000000000
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionException.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Form } from '@inertiajs/react'
-import { useState } from 'react'
-
-export default function PrecognitionException() {
- const [exceptionCaught, setExceptionCaught] = useState(false)
- const [exceptionMessage, setExceptionMessage] = useState('')
-
- const handleException = (error: Error) => {
- setExceptionCaught(true)
- setExceptionMessage(error.message || 'Unknown error')
- }
-
- return (
-
-
Precognition - onException
-
-
- {({ validate, validating }) => (
- <>
- {validating && Validating...
}
- {exceptionCaught && Exception caught: {exceptionMessage}
}
-
-
-
-
-
- {/* This will trigger a validation request to a non-existent endpoint */}
-
- validate('name', {
- onException: handleException,
- })
- }
- >
- Validate with Exception Handler
-
-
- Submit
- >
- )}
-
-
- )
-}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
index acd97449e..246a6268c 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
@@ -4,11 +4,19 @@
let successCalled = false
let errorCalled = false
let finishCalled = false
+ let exceptionCaught = false
+ let exceptionMessage = ''
+
+ const handleException = (error: Error) => {
+ exceptionCaught = true
+ exceptionMessage = error.message || 'Unknown error'
+ }
-
Form Precognition Callbacks
+
Form Precognition Callbacks & Exceptions
+
Callbacks Test
{#if validating}
Validating...
@@ -61,4 +69,33 @@
Validate with onError
+
+
+
+
Exception Test
+
+ {#if validating}
+ Validating...
+ {/if}
+ {#if exceptionCaught}
+ Exception caught: {exceptionMessage}
+ {/if}
+
+
+
+
+
+
+
+ validate('name', {
+ onException: handleException,
+ })}
+ >
+ Validate with Exception Handler
+
+
+ Submit
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte
deleted file mode 100644
index ea3d17b14..000000000
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionException.svelte
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
Precognition - onException
-
-
- {#if validating}
- Validating...
- {/if}
- {#if exceptionCaught}
- Exception caught: {exceptionMessage}
- {/if}
-
-
-
-
-
-
-
- validate('name', {
- onException: handleException,
- })}
- >
- Validate with Exception Handler
-
-
- Submit
-
-
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
index 3494406e9..ce2760d38 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
@@ -5,12 +5,20 @@ import { ref } from 'vue'
const successCalled = ref(false)
const errorCalled = ref(false)
const finishCalled = ref(false)
+const exceptionCaught = ref(false)
+const exceptionMessage = ref('')
+
+const handleException = (error: Error) => {
+ exceptionCaught.value = true
+ exceptionMessage.value = error.message || 'Unknown error'
+}
-
Form Precognition Callbacks
+
Form Precognition Callbacks & Exceptions
+
Callbacks Test
Validating...
@@ -65,5 +73,31 @@ const finishCalled = ref(false)
+
+
+
+
Exception Test
+
+ Validating...
+ Exception caught: {{ exceptionMessage }}
+
+
+
+
+
+
+
+ Validate with Exception Handler
+
+
+ Submit
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue
deleted file mode 100644
index a4039fe9e..000000000
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionException.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
Precognition - onException
-
-
- Validating...
- Exception caught: {{ exceptionMessage }}
-
-
-
-
-
-
-
- Validate with Exception Handler
-
-
- Submit
-
-
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index ea5ecda35..343c91336 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1933,7 +1933,7 @@ test.describe('Form Component', () => {
})
test('onException handles non-422 errors during validation', async ({ page }) => {
- await page.goto('/form-component/precognition-exception')
+ await page.goto('/form-component/precognition-callbacks')
await page.fill('#name-input', 'John')
From 8343809f296e63be28fbfef42fdbb0504bb43858 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 9 Oct 2025 14:33:20 +0200
Subject: [PATCH 044/145] Unify tests
---
.../FormComponent/PrecognitionCancel.tsx | 27 ++++++++++++++++--
.../PrecognitionManualCancel.tsx | 26 -----------------
.../FormComponent/PrecognitionCancel.svelte | 28 +++++++++++++++++--
.../PrecognitionManualCancel.svelte | 27 ------------------
.../FormComponent/PrecognitionCancel.vue | 24 ++++++++++++++--
.../PrecognitionManualCancel.vue | 25 -----------------
tests/form-component.spec.ts | 14 +++++-----
7 files changed, 79 insertions(+), 92 deletions(-)
delete mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx
delete mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte
delete mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
index 3ed805956..35d685d35 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
@@ -1,17 +1,18 @@
import { Form } from '@inertiajs/react'
-export default function PrecognitionCancel() {
+export default () => {
return (
-
Precognition - Auto Cancel
+
Precognition - Cancel Tests
+
Auto Cancel Test
{({ invalid, errors, validate, validating }) => (
<>
{validating && Validating...
}
@@ -19,6 +20,26 @@ export default function PrecognitionCancel() {
>
)}
+
+
+
+
Manual Cancel Test
+
+ {({ validate, cancelValidation, validating }) => (
+ <>
+ {validating && Validating...
}
+
+
+ validate('name')} />
+
+
+ cancelValidation()}>
+ Cancel Validation
+
+ Submit
+ >
+ )}
+
)
}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx
deleted file mode 100644
index 5624a7e80..000000000
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionManualCancel.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Form } from '@inertiajs/react'
-
-export default function PrecognitionManualCancel() {
- return (
-
-
Precognition - Manual Cancel
-
-
- {({ validate, cancelValidation, validating }) => (
- <>
- {validating && Validating...
}
-
-
- validate('name')} />
-
-
- cancelValidation()}>
- Cancel Validation
-
- Submit
- >
- )}
-
-
- )
-}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
index 046a3b3c5..8c9212503 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
@@ -3,8 +3,9 @@
-
Precognition - Auto Cancel
+
Precognition - Cancel Tests
+
Auto Cancel Test
- validate('name')} />
+ validate('name')} />
{#if invalid('name')}
{errors.name}
@@ -29,4 +30,27 @@
Submit
+
+
+
+
Manual Cancel Test
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+ validate('name')} />
+
+
+ cancelValidation()}>Cancel Validation
+ Submit
+
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte
deleted file mode 100644
index 2cfe7ca3c..000000000
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionManualCancel.svelte
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
Precognition - Manual Cancel
-
-
- {#if validating}
- Validating...
- {/if}
-
-
- validate('name')} />
-
-
- cancelValidation()}>Cancel Validation
- Submit
-
-
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
index 69eb3b1da..85cb7595c 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
@@ -4,8 +4,9 @@ import { Form } from '@inertiajs/vue3'
-
Precognition - Auto Cancel
+
Precognition - Cancel Tests
+
Auto Cancel Test
Validating...
-
+
{{ errors.name }}
@@ -23,5 +24,24 @@ import { Form } from '@inertiajs/vue3'
Submit
+
+
+
+
Manual Cancel Test
+
+ Validating...
+
+
+
+
+
+ Cancel Validation
+ Submit
+
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue
deleted file mode 100644
index 0a07e6099..000000000
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionManualCancel.vue
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
Precognition - Manual Cancel
-
-
- Validating...
-
-
-
-
-
- Cancel Validation
- Submit
-
-
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 343c91336..03cbcd602 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1967,13 +1967,13 @@ test.describe('Form Component', () => {
requests.listenForFailed(page)
requests.listenForResponses(page)
- await page.fill('#name-input', 'ab')
- await page.locator('#name-input').blur()
+ await page.fill('#auto-cancel-name-input', 'ab')
+ await page.locator('#auto-cancel-name-input').blur()
await expect(page.getByText('Validating...')).toBeVisible()
// Immediately change value and trigger new validation - should cancel the first one
- await page.fill('#name-input', 'xy')
- await page.locator('#name-input').blur()
+ await page.fill('#auto-cancel-name-input', 'xy')
+ await page.locator('#auto-cancel-name-input').blur()
await expect(page.getByText('Validating...')).not.toBeVisible()
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
@@ -1986,12 +1986,12 @@ test.describe('Form Component', () => {
})
test('cancelValidation() cancels in-flight validation and resets validating state', async ({ page }) => {
- await page.goto('/form-component/precognition-manual-cancel')
+ await page.goto('/form-component/precognition-cancel')
requests.listenForFailed(page)
- await page.fill('#name-input', 'ab')
- await page.locator('#name-input').blur()
+ await page.fill('#manual-cancel-name-input', 'ab')
+ await page.locator('#manual-cancel-name-input').blur()
await expect(page.getByText('Validating...')).toBeVisible()
await page.getByText('Cancel Validation').click()
From eec5986339639b0fa228dca8ce5dbdf7b49e4c76 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 9 Oct 2025 15:03:08 +0200
Subject: [PATCH 045/145] Unify tests
---
...itionTouch.tsx => PrecognitionMethods.tsx} | 19 ++++++++-
.../Pages/FormComponent/PrecognitionReset.tsx | 40 ------------------
.../FormComponent/PrecognitionValidate.tsx | 34 ---------------
...ouch.svelte => PrecognitionMethods.svelte} | 8 +++-
.../FormComponent/PrecognitionReset.svelte | 42 -------------------
.../FormComponent/PrecognitionValidate.svelte | 39 -----------------
...itionTouch.vue => PrecognitionMethods.vue} | 9 +++-
.../Pages/FormComponent/PrecognitionReset.vue | 37 ----------------
.../FormComponent/PrecognitionValidate.vue | 35 ----------------
tests/form-component.spec.ts | 26 ++++++------
10 files changed, 44 insertions(+), 245 deletions(-)
rename packages/react/test-app/Pages/FormComponent/{PrecognitionTouch.tsx => PrecognitionMethods.tsx} (68%)
delete mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx
delete mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx
rename packages/svelte/test-app/Pages/FormComponent/{PrecognitionTouch.svelte => PrecognitionMethods.svelte} (72%)
delete mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte
delete mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte
rename packages/vue3/test-app/Pages/FormComponent/{PrecognitionTouch.vue => PrecognitionMethods.vue} (74%)
delete mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
delete mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
similarity index 68%
rename from packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
rename to packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
index 9acfbde39..06c294596 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionTouch.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
@@ -3,10 +3,10 @@ import { Form } from '@inertiajs/react'
export default () => {
return (
-
Form Precognition Touch
+
Form Precognition - Touch, Reset & Validate
- {({ invalid, errors, validate, touch, touched, validating }) => (
+ {({ invalid, errors, validate, touch, touched, validating, reset }) => (
<>
{validating && Validating...
}
@@ -27,6 +27,12 @@ export default () => {
validate()}>
Validate All Touched
+ validate('name')}>
+ Validate Name
+
+ validate(['name', 'email'])}>
+ Validate Name and Email
+
touch(['name', 'email'])}>
Touch Name and Email
@@ -39,6 +45,15 @@ export default () => {
>
Touch Name Twice
+ reset()}>
+ Reset All
+
+ reset('name')}>
+ Reset Name
+
+ reset('name', 'email')}>
+ Reset Name and Email
+
>
)}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx
deleted file mode 100644
index 50a267fe3..000000000
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionReset.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Form } from '@inertiajs/react'
-
-export default () => {
- return (
-
-
Form Precognition Reset
-
-
- {({ invalid, errors, validate, touch, validating, reset }) => (
- <>
- {validating && Validating...
}
-
-
-
touch('name')} />
- {invalid('name') &&
{errors.name}
}
-
-
-
-
touch('email')} />
- {invalid('email') &&
{errors.email}
}
-
-
- validate()}>
- Validate All Touched
-
- reset()}>
- Reset All
-
- reset('name')}>
- Reset Name
-
- reset('name', 'email')}>
- Reset Name and Email
-
- >
- )}
-
-
- )
-}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx
deleted file mode 100644
index ae7c47070..000000000
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionValidate.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Form } from '@inertiajs/react'
-
-export default () => {
- return (
-
-
Form Precognition Validate
-
-
- {({ invalid, errors, validate, touch, validating }) => (
- <>
- {validating && Validating...
}
-
-
-
touch('name')} />
- {invalid('name') &&
{errors.name}
}
-
-
-
-
- {invalid('email') &&
{errors.email}
}
-
-
- validate('name')}>
- Validate Name
-
- validate(['name', 'email'])}>
- Validate Name and Email
-
- >
- )}
-
-
- )
-}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTouch.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionMethods.svelte
similarity index 72%
rename from packages/svelte/test-app/Pages/FormComponent/PrecognitionTouch.svelte
rename to packages/svelte/test-app/Pages/FormComponent/PrecognitionMethods.svelte
index a30e3850a..bc2f91a24 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTouch.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionMethods.svelte
@@ -3,7 +3,7 @@
-
Form Precognition Touch
+
Form Precognition - Touch, Reset & Validate
{#if validating}
Validating...
@@ -39,6 +40,8 @@
{touched() ? 'Form has touched fields' : 'Form has no touched fields'}
validate()}>Validate All Touched
+ validate('name')}>Validate Name
+ validate(['name', 'email'])}>Validate Name and Email
touch(['name', 'email'])}>Touch Name and Email
Touch Name Twice
+ reset()}>Reset All
+ reset('name')}>Reset Name
+ reset('name', 'email')}>Reset Name and Email
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte
deleted file mode 100644
index 89b6dbdc0..000000000
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionReset.svelte
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
Form Precognition Reset
-
-
- {#if validating}
- Validating...
- {/if}
-
-
-
touch('name')} />
- {#if invalid('name')}
-
{errors.name}
- {/if}
-
-
-
-
touch('email')} />
- {#if invalid('email')}
-
{errors.email}
- {/if}
-
-
- validate()}>Validate All Touched
- reset()}>Reset All
- reset('name')}>Reset Name
- reset('name', 'email')}>Reset Name and Email
-
-
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte
deleted file mode 100644
index 69f4a7c44..000000000
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionValidate.svelte
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
Form Precognition Validate
-
-
- {#if validating}
- Validating...
- {/if}
-
-
-
touch('name')} />
- {#if invalid('name')}
-
{errors.name}
- {/if}
-
-
-
-
- {#if invalid('email')}
-
{errors.email}
- {/if}
-
-
- validate('name')}>Validate Name
- validate(['name', 'email'])}>Validate Name and Email
-
-
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
similarity index 74%
rename from packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
rename to packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
index dd77870af..7e8bf30f5 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTouch.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
@@ -4,13 +4,13 @@ import { Form } from '@inertiajs/vue3'
-
Form Precognition Touch
+
Form Precognition - Touch, Reset & Validate
Validating...
@@ -33,6 +33,8 @@ import { Form } from '@inertiajs/vue3'
{{ touched() ? 'Form has touched fields' : 'Form has no touched fields' }}
Validate All Touched
+ Validate Name
+ Validate Name and Email
Touch Name and Email
Touch Name Twice
+ Reset All
+ Reset Name
+ Reset Name and Email
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
deleted file mode 100644
index 472569b1b..000000000
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionReset.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
Form Precognition Reset
-
-
- Validating...
-
-
-
-
- {{ errors.name }}
-
-
-
-
-
-
- {{ errors.email }}
-
-
-
- Validate All Touched
- Reset All
- Reset Name
- Reset Name and Email
-
-
-
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
deleted file mode 100644
index 0398592ee..000000000
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionValidate.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
Form Precognition Validate
-
-
- Validating...
-
-
-
-
- {{ errors.name }}
-
-
-
-
-
-
- {{ errors.email }}
-
-
-
- Validate Name
- Validate Name and Email
-
-
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 03cbcd602..af20817ee 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1621,7 +1621,7 @@ test.describe('Form Component', () => {
})
test('validates all touched fields when calling validate() without arguments', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1643,7 +1643,7 @@ test.describe('Form Component', () => {
})
test('reset all fields clears all touched fields', async ({ page }) => {
- await page.goto('/form-component/precognition-reset')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1662,7 +1662,7 @@ test.describe('Form Component', () => {
})
test('reset specific fields removes only those fields from touched', async ({ page }) => {
- await page.goto('/form-component/precognition-reset')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1690,7 +1690,7 @@ test.describe('Form Component', () => {
})
test('touch with array marks multiple fields as touched', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.fill('input[name="email"]', 'x')
@@ -1706,7 +1706,7 @@ test.describe('Form Component', () => {
})
test('touch deduplicates fields when called multiple times', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
@@ -1721,7 +1721,7 @@ test.describe('Form Component', () => {
})
test('touched() returns false when no fields are touched', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-methods')
await expect(page.getByTestId('any-touched')).toHaveText('Form has no touched fields')
await expect(page.getByTestId('name-touched')).toHaveText('Name is not touched')
@@ -1729,7 +1729,7 @@ test.describe('Form Component', () => {
})
test('touched(field) returns true when specific field is touched', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-methods')
await page.locator('input[name="name"]').focus()
await page.locator('input[name="name"]').blur()
@@ -1740,7 +1740,7 @@ test.describe('Form Component', () => {
})
test('touched() returns true when any field is touched', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-methods')
await page.locator('input[name="email"]').focus()
await page.locator('input[name="email"]').blur()
@@ -1751,7 +1751,7 @@ test.describe('Form Component', () => {
})
test('touched() updates when multiple fields are touched', async ({ page }) => {
- await page.goto('/form-component/precognition-touch')
+ await page.goto('/form-component/precognition-methods')
await page.locator('input[name="name"]').focus()
await page.locator('input[name="name"]').blur()
@@ -1764,7 +1764,7 @@ test.describe('Form Component', () => {
})
test('validate with specific field works independently of touched state', async ({ page }) => {
- await page.goto('/form-component/precognition-validate')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.fill('input[name="email"]', 'x')
@@ -1779,7 +1779,7 @@ test.describe('Form Component', () => {
})
test('validate with array of fields validates multiple fields', async ({ page }) => {
- await page.goto('/form-component/precognition-validate')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.fill('input[name="email"]', 'x')
@@ -1794,7 +1794,7 @@ test.describe('Form Component', () => {
})
test('reset with array removes multiple fields from touched', async ({ page }) => {
- await page.goto('/form-component/precognition-reset')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1824,7 +1824,7 @@ test.describe('Form Component', () => {
})
test('touching one field and validating another does not validate the touched field', async ({ page }) => {
- await page.goto('/form-component/precognition-validate')
+ await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
From a98ecb4258202dc946f63b21ef8460c11ef67060 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 12:01:21 +0200
Subject: [PATCH 046/145] Refine test suite
---
.../Pages/FormComponent/Precognition.tsx | 8 ++---
.../FormComponent/PrecognitionAllErrors.tsx | 16 +++++-----
.../FormComponent/PrecognitionArrayErrors.tsx | 8 ++---
.../FormComponent/PrecognitionCallbacks.tsx | 16 +++++-----
.../FormComponent/PrecognitionCancel.tsx | 12 +++----
.../FormComponent/PrecognitionDefaults.tsx | 6 ++--
.../Pages/FormComponent/PrecognitionFiles.tsx | 6 ++--
.../FormComponent/PrecognitionHeaders.tsx | 6 ++--
.../FormComponent/PrecognitionMethods.tsx | 14 ++++----
.../FormComponent/PrecognitionTransform.tsx | 6 ++--
.../Pages/FormComponent/Precognition.svelte | 12 +++----
.../PrecognitionAllErrors.svelte | 20 ++++++------
.../PrecognitionArrayErrors.svelte | 12 +++----
.../PrecognitionCallbacks.svelte | 16 +++++-----
.../FormComponent/PrecognitionCancel.svelte | 18 +++++------
.../FormComponent/PrecognitionDefaults.svelte | 10 +++---
.../FormComponent/PrecognitionFiles.svelte | 10 +++---
.../FormComponent/PrecognitionHeaders.svelte | 10 +++---
.../FormComponent/PrecognitionMethods.svelte | 18 +++++------
.../PrecognitionTransform.svelte | 10 +++---
.../Pages/FormComponent/Precognition.vue | 8 ++---
.../FormComponent/PrecognitionAllErrors.vue | 16 +++++-----
.../FormComponent/PrecognitionArrayErrors.vue | 8 ++---
.../FormComponent/PrecognitionCallbacks.vue | 16 +++++-----
.../FormComponent/PrecognitionCancel.vue | 12 +++----
.../FormComponent/PrecognitionDefaults.vue | 6 ++--
.../Pages/FormComponent/PrecognitionFiles.vue | 6 ++--
.../FormComponent/PrecognitionHeaders.vue | 6 ++--
.../FormComponent/PrecognitionMethods.vue | 14 ++++----
.../FormComponent/PrecognitionTransform.vue | 6 ++--
tests/form-component.spec.ts | 32 +++++++++----------
31 files changed, 182 insertions(+), 182 deletions(-)
diff --git a/packages/react/test-app/Pages/FormComponent/Precognition.tsx b/packages/react/test-app/Pages/FormComponent/Precognition.tsx
index 845aecd63..2ff56745b 100644
--- a/packages/react/test-app/Pages/FormComponent/Precognition.tsx
+++ b/packages/react/test-app/Pages/FormComponent/Precognition.tsx
@@ -8,19 +8,19 @@ export default () => {
{({ invalid, errors, validate, valid, validating }) => (
<>
- {validating && Validating...
}
-
-
validate('name')} />
+
validate('name')} />
{invalid('name') &&
{errors.name}
}
{valid('name') &&
Name is valid!
}
-
validate('email')} />
+
validate('email')} />
{invalid('email') &&
{errors.email}
}
{valid('email') &&
Email is valid!
}
+
+ {validating && Validating...
}
>
)}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx
index a0be71ede..4b63a3df7 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionAllErrors.tsx
@@ -13,20 +13,18 @@ export default () => {
>
{({ invalid, errors, validate, valid, validating }) => (
<>
- {validating && Validating...
}
-
-
validate('name')} />
+
validate('name')} />
{invalid('name') && (
{Array.isArray(errors.name) ? (
errors.name.map((error, index) => (
-
+
{error}
))
) : (
-
{errors.name}
+
{errors.name}
)}
)}
@@ -34,22 +32,24 @@ export default () => {
-
validate('email')} />
+
validate('email')} />
{invalid('email') && (
{Array.isArray(errors.email) ? (
errors.email.map((error, index) => (
-
+
{error}
))
) : (
-
{errors.email}
+
{errors.email}
)}
)}
{valid('email') &&
Email is valid!
}
+
+ {validating && Validating...
}
>
)}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx
index d9f35c491..a1a4ffe79 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionArrayErrors.tsx
@@ -8,19 +8,19 @@ export default () => {
{({ invalid, errors, validate, valid, validating }) => (
<>
- {validating && Validating...
}
-
-
validate('name')} />
+
validate('name')} />
{invalid('name') &&
{errors.name}
}
{valid('name') &&
Name is valid!
}
-
validate('email')} />
+
validate('email')} />
{invalid('email') &&
{errors.email}
}
{valid('email') &&
Email is valid!
}
+
+ {validating && Validating...
}
>
)}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
index ba6c70af4..b2e5e583a 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
@@ -21,15 +21,15 @@ export default () => {
{({ validate, validating, touch }) => (
<>
+
+ touch('name')} />
+
+
{validating && Validating...
}
{successCalled && onSuccess called!
}
{errorCalled && onError called!
}
{finishCalled && onFinish called!
}
-
- touch('name')} />
-
-
{
@@ -73,13 +73,13 @@ export default () => {
{({ validate, validating }) => (
<>
- {validating && Validating...
}
- {exceptionCaught && Exception caught: {exceptionMessage}
}
-
-
+
+ {validating && Validating...
}
+ {exceptionCaught && Exception caught: {exceptionMessage}
}
+
{/* This will trigger a validation request to a non-existent endpoint */}
{
{({ invalid, errors, validate, validating }) => (
<>
- {validating && Validating...
}
-
+ {validating && Validating...
}
+
Submit
>
)}
@@ -27,12 +27,12 @@ export default () => {
{({ validate, cancelValidation, validating }) => (
<>
- {validating && Validating...
}
-
- validate('name')} />
+ validate('name')} />
+ {validating && Validating...
}
+
cancelValidation()}>
Cancel Validation
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
index a610c96ad..0333b43cb 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
@@ -15,13 +15,13 @@ export default function PrecognitionDefaults() {
{({ invalid, errors, validate, validating }) => (
<>
- {validating && Validating...
}
-
+ {validating && Validating...
}
+
Set Defaults
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
index db7db4b46..2265275c8 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
@@ -16,10 +16,8 @@ export default () => {
>
{({ invalid, errors, validate, valid, validating, touch }) => (
<>
- {validating && Validating...
}
-
-
validate('name')} />
+
validate('name')} />
{invalid('name') &&
{errors.name}
}
{valid('name') &&
Name is valid!
}
@@ -30,6 +28,8 @@ export default () => {
{valid('avatar') && Avatar is valid!
}
+ {validating && Validating...
}
+
setValidateFilesEnabled(!validateFilesEnabled)}>
Toggle Validate Files ({validateFilesEnabled ? 'enabled' : 'disabled'})
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx
index 931fd6231..edbd99d9f 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionHeaders.tsx
@@ -13,13 +13,13 @@ export default function PrecognitionHeaders() {
>
{({ invalid, errors, validate, validating }) => (
<>
- {validating && Validating...
}
-
+ {validating && Validating...
}
+
Submit
>
)}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
index 06c294596..3fb5fdca3 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
@@ -8,21 +8,21 @@ export default () => {
{({ invalid, errors, validate, touch, touched, validating, reset }) => (
<>
- {validating && Validating...
}
-
- {touched('name') ? 'Name is touched' : 'Name is not touched'}
- {touched('email') ? 'Email is touched' : 'Email is not touched'}
- {touched() ? 'Form has touched fields' : 'Form has no touched fields'}
+ {validating && Validating...
}
+
+ {touched('name') ? 'Name is touched' : 'Name is not touched'}
+ {touched('email') ? 'Email is touched' : 'Email is not touched'}
+ {touched() ? 'Form has touched fields' : 'Form has no touched fields'}
validate()}>
Validate All Touched
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
index 4eb783f61..193e5dde3 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
@@ -13,13 +13,13 @@ export default () => {
>
{({ invalid, errors, validate, valid, validating }) => (
<>
- {validating && Validating...
}
-
-
validate('name')} />
+
validate('name')} />
{invalid('name') &&
{errors.name}
}
{valid('name') &&
Name is valid!
}
+
+ {validating && Validating...
}
>
)}
diff --git a/packages/svelte/test-app/Pages/FormComponent/Precognition.svelte b/packages/svelte/test-app/Pages/FormComponent/Precognition.svelte
index c9938186c..42a41c237 100644
--- a/packages/svelte/test-app/Pages/FormComponent/Precognition.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/Precognition.svelte
@@ -15,12 +15,8 @@
let:valid
let:validating
>
- {#if validating}
- Validating...
- {/if}
-
-
validate('email')} />
+
validate('email')} />
{#if invalid('email')}
{errors.email}
{/if}
@@ -38,5 +34,9 @@
Email is valid!
{/if}
+
+ {#if validating}
+ Validating...
+ {/if}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte
index 647b3a955..cf4c16f2b 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionAllErrors.svelte
@@ -16,20 +16,16 @@
let:valid
let:validating
>
- {#if validating}
- Validating...
- {/if}
-
-
validate('name')} />
+
validate('name')} />
{#if invalid('name')}
{#if Array.isArray(errors.name)}
{#each errors.name as error, index}
-
{error}
+
{error}
{/each}
{:else}
-
{errors.name}
+
{errors.name}
{/if}
{/if}
@@ -39,15 +35,15 @@
-
validate('email')} />
+
validate('email')} />
{#if invalid('email')}
{#if Array.isArray(errors.email)}
{#each errors.email as error, index}
-
{error}
+
{error}
{/each}
{:else}
-
{errors.email}
+
{errors.email}
{/if}
{/if}
@@ -55,5 +51,9 @@
Email is valid!
{/if}
+
+ {#if validating}
+ Validating...
+ {/if}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte
index a36918728..252e533ca 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionArrayErrors.svelte
@@ -15,12 +15,8 @@
let:valid
let:validating
>
- {#if validating}
- Validating...
- {/if}
-
-
validate('email')} />
+
validate('email')} />
{#if invalid('email')}
{errors.email}
{/if}
@@ -38,5 +34,9 @@
Email is valid!
{/if}
+
+ {#if validating}
+ Validating...
+ {/if}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
index 246a6268c..5941c4782 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
@@ -18,6 +18,10 @@
Callbacks Test
+
+ touch('name')} />
+
+
{#if validating}
Validating...
{/if}
@@ -31,10 +35,6 @@
onFinish called!
{/if}
-
- touch('name')} />
-
-
{
@@ -74,6 +74,10 @@
Exception Test
+
+
+
+
{#if validating}
Validating...
{/if}
@@ -81,10 +85,6 @@
Exception caught: {exceptionMessage}
{/if}
-
-
-
-
- {#if validating}
- Validating...
- {/if}
-
+ {#if validating}
+ Validating...
+ {/if}
+
Submit
@@ -42,14 +42,14 @@
let:cancelValidation
let:validating
>
+
+ validate('name')} />
+
+
{#if validating}
Validating...
{/if}
-
- validate('name')} />
-
-
cancelValidation()}>Cancel Validation
Submit
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
index 2a2cfd3b7..31475a453 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
@@ -21,12 +21,8 @@
let:validate
let:validating
>
- {#if validating}
- Validating...
- {/if}
-
+ {#if validating}
+ Validating...
+ {/if}
+
Set Defaults
validate('name')}>Validate Name
Submit
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte
index fa6569d63..7c9e8bfa7 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionFiles.svelte
@@ -19,12 +19,8 @@
let:validating
let:touch
>
- {#if validating}
- Validating...
- {/if}
-
+ {#if validating}
+ Validating...
+ {/if}
+
(validateFilesEnabled = !validateFilesEnabled)}>
Toggle Validate Files ({validateFilesEnabled ? 'enabled' : 'disabled'})
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte
index 6fb67a051..ada432b86 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionHeaders.svelte
@@ -15,12 +15,8 @@
let:validate
let:validating
>
- {#if validating}
- Validating...
- {/if}
-
+ {#if validating}
+ Validating...
+ {/if}
+
Submit
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionMethods.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionMethods.svelte
index bc2f91a24..199a121e8 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionMethods.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionMethods.svelte
@@ -17,27 +17,27 @@
let:validating
let:reset
>
- {#if validating}
- Validating...
- {/if}
-
- {touched('name') ? 'Name is touched' : 'Name is not touched'}
- {touched('email') ? 'Email is touched' : 'Email is not touched'}
- {touched() ? 'Form has touched fields' : 'Form has no touched fields'}
+ {#if validating}
+ Validating...
+ {/if}
+
+ {touched('name') ? 'Name is touched' : 'Name is not touched'}
+ {touched('email') ? 'Email is touched' : 'Email is not touched'}
+ {touched() ? 'Form has touched fields' : 'Form has no touched fields'}
validate()}>Validate All Touched
validate('name')}>Validate Name
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
index 265f17308..6dc294178 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
@@ -16,12 +16,8 @@
let:valid
let:validating
>
- {#if validating}
- Validating...
- {/if}
-
-
validate('name')} />
+
validate('name')} />
{#if invalid('name')}
{errors.name}
{/if}
@@ -29,5 +25,9 @@
Name is valid!
{/if}
+
+ {#if validating}
+ Validating...
+ {/if}
diff --git a/packages/vue3/test-app/Pages/FormComponent/Precognition.vue b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
index 32aabe1a5..ce7ae4213 100644
--- a/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/Precognition.vue
@@ -12,10 +12,8 @@ import { Form } from '@inertiajs/vue3'
:validate-timeout="100"
#default="{ invalid, errors, validate, valid, validating }"
>
- Validating...
-
+
+ Validating...
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue
index b88737c29..9e4519de0 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionAllErrors.vue
@@ -13,33 +13,33 @@ import { Form } from '@inertiajs/vue3'
:simple-validation-errors="false"
#default="{ invalid, errors, validate, valid, validating }"
>
- Validating...
-
-
+
-
+
{{ error }}
-
{{ errors.name }}
+
{{ errors.name }}
Name is valid!
-
+
-
+
{{ error }}
-
{{ errors.email }}
+
{{ errors.email }}
Email is valid!
+
+ Validating...
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue
index 724829dbc..559d650a2 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionArrayErrors.vue
@@ -12,10 +12,8 @@ import { Form } from '@inertiajs/vue3'
:validate-timeout="100"
#default="{ invalid, errors, validate, valid, validating }"
>
- Validating...
-
+
+ Validating...
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
index ce2760d38..33fe56981 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
@@ -21,15 +21,15 @@ const handleException = (error: Error) => {
Callbacks Test
+
+ touch('name')" />
+
+
Validating...
onSuccess called!
onError called!
onFinish called!
-
- touch('name')" />
-
-
{
Exception Test
- Validating...
- Exception caught: {{ exceptionMessage }}
-
-
+
+ Validating...
+ Exception caught: {{ exceptionMessage }}
+
- Validating...
-
+ Validating...
+
Submit
@@ -34,12 +34,12 @@ import { Form } from '@inertiajs/vue3'
:validate-timeout="5000"
#default="{ validate, cancelValidation, validating }"
>
- Validating...
-
-
+
+ Validating...
+
Cancel Validation
Submit
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
index 205af63b7..4341388a4 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
@@ -20,15 +20,15 @@ const handleSetDefaults = () => {
:validate-timeout="100"
#default="{ invalid, errors, validate, validating }"
>
- Validating...
-
+ Validating...
+
Set Defaults
Validate Name
Submit
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
index 271e118a2..cb2a7e14b 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
@@ -16,10 +16,8 @@ const validateFilesEnabled = ref(false)
:validate-files="validateFilesEnabled"
#default="{ invalid, errors, validate, valid, validating, touch }"
>
- Validating...
-
+ Validating...
+
Toggle Validate Files ({{ validateFilesEnabled ? 'enabled' : 'disabled' }})
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue
index bcb0dbbdd..b006055e7 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionHeaders.vue
@@ -13,15 +13,15 @@ import { Form } from '@inertiajs/vue3'
:validate-timeout="100"
#default="{ invalid, errors, validate, validating }"
>
- Validating...
-
+ Validating...
+
Submit
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
index 7e8bf30f5..af27c4991 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
@@ -12,25 +12,25 @@ import { Form } from '@inertiajs/vue3'
:validate-timeout="100"
#default="{ invalid, errors, validate, touch, touched, validating, reset }"
>
- Validating...
-
- {{ touched('name') ? 'Name is touched' : 'Name is not touched' }}
- {{ touched('email') ? 'Email is touched' : 'Email is not touched' }}
- {{ touched() ? 'Form has touched fields' : 'Form has no touched fields' }}
+ Validating...
+
+ {{ touched('name') ? 'Name is touched' : 'Name is not touched' }}
+ {{ touched('email') ? 'Email is touched' : 'Email is not touched' }}
+ {{ touched() ? 'Form has touched fields' : 'Form has no touched fields' }}
Validate All Touched
Validate Name
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
index 8f9bb6cd4..c2778463b 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
@@ -13,13 +13,13 @@ import { Form } from '@inertiajs/vue3'
:transform="(data) => ({ name: String(data.name || '').toUpperCase() })"
>
- Validating...
-
+
+ Validating...
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index af20817ee..11a456626 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1467,8 +1467,8 @@ test.describe('Form Component', () => {
await expect(page.getByText('Validating...')).not.toBeVisible()
// Should show all errors from the array
- await expect(page.getByTestId('name-error-0')).toHaveText('The name must be at least 3 characters.')
- await expect(page.getByTestId('name-error-1')).toHaveText('The name contains invalid characters.')
+ await expect(page.locator('#name-error-0')).toHaveText('The name must be at least 3 characters.')
+ await expect(page.locator('#name-error-1')).toHaveText('The name contains invalid characters.')
})
test('shows all email errors when simpleValidationErrors is false', async ({ page }) => {
@@ -1481,8 +1481,8 @@ test.describe('Form Component', () => {
await expect(page.getByText('Validating...')).not.toBeVisible()
// Should show all errors from the array
- await expect(page.getByTestId('email-error-0')).toHaveText('The email must be a valid email address.')
- await expect(page.getByTestId('email-error-1')).toHaveText('The email format is incorrect.')
+ await expect(page.locator('#email-error-0')).toHaveText('The email must be a valid email address.')
+ await expect(page.locator('#email-error-1')).toHaveText('The email format is incorrect.')
})
test('clears validation error when field becomes valid', async ({ page }) => {
@@ -1723,9 +1723,9 @@ test.describe('Form Component', () => {
test('touched() returns false when no fields are touched', async ({ page }) => {
await page.goto('/form-component/precognition-methods')
- await expect(page.getByTestId('any-touched')).toHaveText('Form has no touched fields')
- await expect(page.getByTestId('name-touched')).toHaveText('Name is not touched')
- await expect(page.getByTestId('email-touched')).toHaveText('Email is not touched')
+ await expect(page.locator('#any-touched')).toHaveText('Form has no touched fields')
+ await expect(page.locator('#name-touched')).toHaveText('Name is not touched')
+ await expect(page.locator('#email-touched')).toHaveText('Email is not touched')
})
test('touched(field) returns true when specific field is touched', async ({ page }) => {
@@ -1734,9 +1734,9 @@ test.describe('Form Component', () => {
await page.locator('input[name="name"]').focus()
await page.locator('input[name="name"]').blur()
- await expect(page.getByTestId('name-touched')).toHaveText('Name is touched')
- await expect(page.getByTestId('email-touched')).toHaveText('Email is not touched')
- await expect(page.getByTestId('any-touched')).toHaveText('Form has touched fields')
+ await expect(page.locator('#name-touched')).toHaveText('Name is touched')
+ await expect(page.locator('#email-touched')).toHaveText('Email is not touched')
+ await expect(page.locator('#any-touched')).toHaveText('Form has touched fields')
})
test('touched() returns true when any field is touched', async ({ page }) => {
@@ -1745,9 +1745,9 @@ test.describe('Form Component', () => {
await page.locator('input[name="email"]').focus()
await page.locator('input[name="email"]').blur()
- await expect(page.getByTestId('any-touched')).toHaveText('Form has touched fields')
- await expect(page.getByTestId('email-touched')).toHaveText('Email is touched')
- await expect(page.getByTestId('name-touched')).toHaveText('Name is not touched')
+ await expect(page.locator('#any-touched')).toHaveText('Form has touched fields')
+ await expect(page.locator('#email-touched')).toHaveText('Email is touched')
+ await expect(page.locator('#name-touched')).toHaveText('Name is not touched')
})
test('touched() updates when multiple fields are touched', async ({ page }) => {
@@ -1758,9 +1758,9 @@ test.describe('Form Component', () => {
await page.locator('input[name="email"]').focus()
await page.locator('input[name="email"]').blur()
- await expect(page.getByTestId('name-touched')).toHaveText('Name is touched')
- await expect(page.getByTestId('email-touched')).toHaveText('Email is touched')
- await expect(page.getByTestId('any-touched')).toHaveText('Form has touched fields')
+ await expect(page.locator('#name-touched')).toHaveText('Name is touched')
+ await expect(page.locator('#email-touched')).toHaveText('Email is touched')
+ await expect(page.locator('#any-touched')).toHaveText('Form has touched fields')
})
test('validate with specific field works independently of touched state', async ({ page }) => {
From b68d8e1d5bf691125ff85a3e044f427013e5cb87 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 13:12:17 +0200
Subject: [PATCH 047/145] Refine tests
---
.../FormComponent/PrecognitionTransform.tsx | 4 +-
.../PrecognitionTransform.svelte | 4 +-
.../FormComponent/PrecognitionTransform.vue | 4 +-
tests/app/server.js | 32 -----
tests/form-component.spec.ts | 123 ++++++++----------
5 files changed, 62 insertions(+), 105 deletions(-)
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
index 193e5dde3..ced7a4552 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionTransform.tsx
@@ -6,10 +6,10 @@ export default () => {
Form Precognition Transform
({ name: String(data.name || '').toUpperCase() })}
+ transform={(data) => ({ name: String(data.name || '').repeat(2) })}
>
{({ invalid, errors, validate, valid, validating }) => (
<>
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
index 6dc294178..a4f2f0785 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionTransform.svelte
@@ -6,10 +6,10 @@
Form Precognition Transform
({ name: String(data.name || '').toUpperCase() })}
+ transform={(data) => ({ name: String(data.name || '').repeat(2) })}
let:invalid
let:errors
let:validate
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
index c2778463b..03a97bc96 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionTransform.vue
@@ -7,10 +7,10 @@ import { Form } from '@inertiajs/vue3'
Form Precognition Transform
diff --git a/tests/app/server.js b/tests/app/server.js
index 039c5a968..1041482a4 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -1035,38 +1035,6 @@ app.post('/form-component/precognition-files', upload.any(), (req, res) => {
}, 500)
})
-app.post('/form-component/precognition-transform', (req, res) => {
- setTimeout(() => {
- const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
- const name = req.body['name']
- const errors = {}
-
- // Validate that name is uppercase
- if (!name) {
- errors.name = 'The name field is required.'
- } else if (name !== name.toUpperCase()) {
- errors.name = 'The name must be uppercase.'
- }
-
- if (only.length) {
- Object.keys(errors).forEach((key) => {
- if (!only.includes(key)) {
- delete errors[key]
- }
- })
- }
-
- res.header('Precognition', 'true')
- res.header('Vary', 'Precognition')
-
- if (Object.keys(errors).length) {
- return res.status(422).json({ errors })
- }
-
- return res.status(204).header('Precognition-Success', 'true').send()
- }, 500)
-})
-
app.post('/form-component/precognition-headers', (req, res) => {
setTimeout(() => {
const customHeader = req.headers['x-custom-header']
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 11a456626..353e0c76e 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1443,48 +1443,6 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
})
- test('shows only first error when server returns errors as array', async ({ page }) => {
- await page.goto('/form-component/precognition-array-errors')
-
- await page.fill('input[name="name"]', 'ab')
- await page.locator('input[name="name"]').blur()
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Should show only the first error from the array, not the second
- await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
- await expect(page.getByText('The name contains invalid characters.')).not.toBeVisible()
- })
-
- test('shows all errors when simpleValidationErrors is false', async ({ page }) => {
- await page.goto('/form-component/precognition-all-errors')
-
- await page.fill('input[name="name"]', 'ab')
- await page.locator('input[name="name"]').blur()
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Should show all errors from the array
- await expect(page.locator('#name-error-0')).toHaveText('The name must be at least 3 characters.')
- await expect(page.locator('#name-error-1')).toHaveText('The name contains invalid characters.')
- })
-
- test('shows all email errors when simpleValidationErrors is false', async ({ page }) => {
- await page.goto('/form-component/precognition-all-errors')
-
- await page.fill('input[name="email"]', 'invalid')
- await page.locator('input[name="email"]').blur()
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Should show all errors from the array
- await expect(page.locator('#email-error-0')).toHaveText('The email must be a valid email address.')
- await expect(page.locator('#email-error-1')).toHaveText('The email format is incorrect.')
- })
-
test('clears validation error when field becomes valid', async ({ page }) => {
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1535,7 +1493,7 @@ test.describe('Form Component', () => {
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
})
- test('valid returns false before validation', async ({ page }) => {
+ test('field is valid when validated and no errors exist', async ({ page }) => {
await expect(page.getByText('Name is valid!')).not.toBeVisible()
await page.fill('input[name="name"]', 'John Doe')
@@ -1545,21 +1503,9 @@ test.describe('Form Component', () => {
await expect(page.getByText('Validating...')).not.toBeVisible()
await expect(page.getByText('Name is valid!')).toBeVisible()
- await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
})
- test('valid returns false when field has validation errors', async ({ page }) => {
- await page.fill('input[name="name"]', 'ab')
- await page.locator('input[name="name"]').blur()
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
- await expect(page.getByText('Name is valid!')).not.toBeVisible()
- })
-
- test('valid returns true after successful validation', async ({ page }) => {
+ test('field is not valid before validation', async ({ page }) => {
await expect(page.getByText('Name is valid!')).not.toBeVisible()
await page.fill('input[name="name"]', 'John Doe')
@@ -1569,11 +1515,10 @@ test.describe('Form Component', () => {
await expect(page.getByText('Validating...')).not.toBeVisible()
await expect(page.getByText('Name is valid!')).toBeVisible()
- })
-
- test('invalid returns true only when validation errors exist', async ({ page }) => {
await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ })
+ test('field is not valid after failed validation', async ({ page }) => {
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
@@ -1581,9 +1526,10 @@ test.describe('Form Component', () => {
await expect(page.getByText('Validating...')).not.toBeVisible()
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
})
- test('valid persists after successful validation', async ({ page }) => {
+ test('valid field persists after successful validation', async ({ page }) => {
await page.fill('input[name="name"]', 'John Doe')
await page.locator('input[name="name"]').blur()
@@ -1601,7 +1547,7 @@ test.describe('Form Component', () => {
await expect(page.getByText('Name is valid!')).toBeVisible()
})
- test('valid becomes false when field is re-validated with errors', async ({ page }) => {
+ test('valid field becomes invalid when field is revalidated with errors', async ({ page }) => {
await page.fill('input[name="name"]', 'John Doe')
await page.locator('input[name="name"]').blur()
@@ -1620,6 +1566,34 @@ test.describe('Form Component', () => {
await expect(page.getByText('Name is valid!')).not.toBeVisible()
})
+ test('shows only first error when server returns errors as array', async ({ page }) => {
+ await page.goto('/form-component/precognition-array-errors')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show only the first error from the array, not the second
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The name contains invalid characters.')).not.toBeVisible()
+ })
+
+ test('shows all errors when simpleValidationErrors is false', async ({ page }) => {
+ await page.goto('/form-component/precognition-all-errors')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show all errors from the array
+ await expect(page.locator('#name-error-0')).toHaveText('The name must be at least 3 characters.')
+ await expect(page.locator('#name-error-1')).toHaveText('The name contains invalid characters.')
+ })
+
test('validates all touched fields when calling validate() without arguments', async ({ page }) => {
await page.goto('/form-component/precognition-methods')
@@ -1652,13 +1626,20 @@ test.describe('Form Component', () => {
await page.locator('input[name="email"]').blur()
await page.getByRole('button', { name: 'Reset All' }).click()
+
+ await expect(page.locator('input[name="name"]')).toHaveValue('')
+ await expect(page.locator('input[name="email"]')).toHaveValue('')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
await page.getByRole('button', { name: 'Validate All Touched' }).click()
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
- await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).not.toBeVisible()
})
test('reset specific fields removes only those fields from touched', async ({ page }) => {
@@ -1678,14 +1659,14 @@ test.describe('Form Component', () => {
await expect(page.locator('input[name="name"]')).toHaveValue('')
await expect(page.locator('input[name="email"]')).toHaveValue('x')
- await page.fill('input[name="name"]', 'abc')
+ await page.fill('input[name="email"]', 'y')
await page.getByRole('button', { name: 'Validate All Touched' }).click()
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('The name field is required.')).not.toBeVisible()
await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
})
@@ -1882,13 +1863,21 @@ test.describe('Form Component', () => {
test('transforms data for validation requests', async ({ page }) => {
await page.goto('/form-component/precognition-transform')
- await page.fill('input[name="name"]', 'taylor')
+ await page.fill('input[name="name"]', 'a')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'aa')
await page.locator('input[name="name"]').blur()
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- // Should succeed because transform converts to uppercase
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
await expect(page.getByText('Name is valid!')).toBeVisible()
})
From 68d7ed307efc093b34fc9dc6a0775090bdd86ae0 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 13:51:43 +0200
Subject: [PATCH 048/145] Pass validation timeout
---
packages/core/src/precognition.ts | 9 +++++----
packages/react/src/Form.ts | 3 ++-
packages/svelte/src/components/Form.svelte | 1 +
packages/vue3/src/form.ts | 1 +
tests/form-component.spec.ts | 4 +++-
5 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index c12a8af88..6bfef93b3 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -6,6 +6,7 @@ import { objectToFormData } from './formData'
import { Errors, FormComponentValidateOptions, RequestData, Visit } from './types'
interface UsePrecognitionOptions {
+ timeout: number
onStart: () => void
onFinish: () => void
}
@@ -27,7 +28,7 @@ interface PrecognitionValidator {
}
export default function usePrecognition(precognitionOptions: UsePrecognitionOptions): PrecognitionValidator {
- let debounceTimeoutDuration = 1500
+ let debounceTimeoutDuration = precognitionOptions.timeout
let validateFiles: boolean = false
let oldData: RequestData = {}
@@ -45,7 +46,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
if (value !== debounceTimeoutDuration) {
cancelAll()
debounceTimeoutDuration = value
- validate = createValidateFunction()
+ validateFunction = createValidateFunction()
}
}
@@ -138,7 +139,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
{ leading: true, trailing: true },
)
- let validate = createValidateFunction()
+ let validateFunction = createValidateFunction()
return {
setOldData: (data) => {
@@ -148,7 +149,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
validateFiles = value
},
setTimeout,
- validate,
+ validate: (options: PrecognitionValidateOptions) => validateFunction(options),
cancelAll,
}
}
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index fef96e720..f01960eb1 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -91,6 +91,7 @@ const Form = forwardRef(
const validator = useMemo(
() =>
usePrecognition({
+ timeout: validateTimeout,
onStart: () => setValidating(true),
onFinish: () => setValidating(false),
}),
@@ -134,7 +135,7 @@ const Form = forwardRef(
useEffect(() => {
validator.setTimeout(validateTimeout)
- }, [validateTimeout, validator])
+ }, [validateTimeout])
useEffect(() => {
updateDataOnValidator()
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 8038889ce..ecd4950f5 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -55,6 +55,7 @@
let touchedFields: string[] = []
const validator = usePrecognition({
+ timeout: validateTimeout,
onStart: () => {
validating = true
},
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index d28376d25..d9f568f61 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -153,6 +153,7 @@ const Form = defineComponent({
const touched = ref([])
const validator = usePrecognition({
+ timeout: props.validateTimeout,
onStart: () => (validating.value = true),
onFinish: () => (validating.value = false),
})
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 353e0c76e..78a26661e 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1480,10 +1480,12 @@ test.describe('Form Component', () => {
test('does not clear unrelated field errors', async ({ page }) => {
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
await page.fill('input[name="email"]', 'x')
await page.locator('input[name="email"]').blur()
- await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
await page.fill('input[name="email"]', 'test@example.com')
From cb24e3e649e502960f012645761b8c9ee5cdefa3 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 15:45:51 +0200
Subject: [PATCH 049/145] Cleanup
---
packages/core/src/precognition.ts | 10 +++-------
packages/react/src/Form.ts | 4 +---
packages/svelte/src/components/Form.svelte | 22 ++++++----------------
packages/vue3/src/form.ts | 2 --
4 files changed, 10 insertions(+), 28 deletions(-)
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index 6bfef93b3..f3865e0f6 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -142,14 +142,10 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
let validateFunction = createValidateFunction()
return {
- setOldData: (data) => {
- oldData = { ...data }
- },
- validateFiles: (value) => {
- validateFiles = value
- },
+ setOldData: (data) => (oldData = { ...data }),
+ validateFiles: (value) => (validateFiles = value),
setTimeout,
- validate: (options: PrecognitionValidateOptions) => validateFunction(options),
+ validate: (options) => validateFunction(options),
cancelAll,
}
}
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index f01960eb1..f57c56ff0 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -135,7 +135,7 @@ const Form = forwardRef(
useEffect(() => {
validator.setTimeout(validateTimeout)
- }, [validateTimeout])
+ }, [validateTimeout, validator])
useEffect(() => {
updateDataOnValidator()
@@ -313,8 +313,6 @@ const Form = forwardRef(
validate,
touch,
touched: isTouched,
- setValidationTimeout: (duration: number) => validator.setTimeout(duration),
- validateFiles: () => validator.validateFiles(true),
cancelValidation: () => {
validator.cancelAll()
setValidating(false)
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index ecd4950f5..8958b1031 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -282,14 +282,6 @@
return $form.errors[field] !== undefined
}
- export function setValidationTimeout(duration: number) {
- validator.setTimeout(duration)
- }
-
- export function validateFilesEnabled() {
- validator.validateFiles(true)
- }
-
export function cancelValidation() {
validator.cancelAll()
validating = false
@@ -316,9 +308,9 @@
$: slotErrors = $form.errors as Errors
// Create reactive slot props that update when state changes
- $: slotValid = (field: string) => validated.includes(field) && slotErrors[field] === undefined
- $: slotInvalid = (field: string) => slotErrors[field] !== undefined
- $: slotTouched = (field?: string) =>
+ $: validMethod = (field: string) => validated.includes(field) && slotErrors[field] === undefined
+ $: invalidMethod = (field: string) => slotErrors[field] !== undefined
+ $: touchedMethod = (field?: string) =>
typeof field === 'string' ? touchedFields.includes(field) : touchedFields.length > 0
@@ -348,11 +340,9 @@
{validating}
{validate}
{touch}
- touched={slotTouched}
- valid={slotValid}
- invalid={slotInvalid}
- {setValidationTimeout}
- {validateFilesEnabled}
+ touched={touchedMethod}
+ valid={validMethod}
+ invalid={invalidMethod}
{cancelValidation}
/>
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index d9f568f61..0978df16e 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -380,8 +380,6 @@ const Form = defineComponent({
validate,
touch,
touched: isTouched,
- setValidationTimeout: (duration: number) => validator.setTimeout(duration),
- validateFiles: () => validator.validateFiles(true),
cancelValidation: () => {
validator.cancelAll()
validating.value = false
From 27aff2b7006b509339a08388607ec760414187cf Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 17:27:35 +0200
Subject: [PATCH 050/145] Vue playground
---
.../Http/Requests/PrecognitionFormRequest.php | 32 +++
.../vue3/resources/js/Components/Layout.vue | 5 +-
.../js/Pages/FormComponentPrecognition.vue | 244 ++++++++++++++++++
playgrounds/vue3/routes/web.php | 12 +
4 files changed, 291 insertions(+), 2 deletions(-)
create mode 100644 playgrounds/vue3/app/Http/Requests/PrecognitionFormRequest.php
create mode 100644 playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
diff --git a/playgrounds/vue3/app/Http/Requests/PrecognitionFormRequest.php b/playgrounds/vue3/app/Http/Requests/PrecognitionFormRequest.php
new file mode 100644
index 000000000..3fc8a6916
--- /dev/null
+++ b/playgrounds/vue3/app/Http/Requests/PrecognitionFormRequest.php
@@ -0,0 +1,32 @@
+|string>
+ */
+ public function rules(): array
+ {
+ sleep(1);
+
+ return [
+ 'name' => ['required', 'string', 'min:3', 'max:255'],
+ 'email' => ['required', 'email', 'max:255'],
+ 'avatar' => ['nullable', 'file', 'image'],
+ ];
+ }
+}
diff --git a/playgrounds/vue3/resources/js/Components/Layout.vue b/playgrounds/vue3/resources/js/Components/Layout.vue
index 88c5bea06..a3f07acb7 100644
--- a/playgrounds/vue3/resources/js/Components/Layout.vue
+++ b/playgrounds/vue3/resources/js/Components/Layout.vue
@@ -22,8 +22,9 @@ const appName = computed(() => page.props.appName)
Home
Users
Article
- Form
- Form Component
+ useForm
+ {{ '' }}
+ Precognition
Logout
External
Async
diff --git a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
new file mode 100644
index 000000000..529c0b005
--- /dev/null
+++ b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+ Form Precognition
+
+
+
+
+
Live Validation & File Uploads
+
+
+
+
+
+ Enable file validation
+
+
+ Timeout:
+
+ 500ms
+ 1000ms
+ 1500ms
+ 2000ms
+
+
+
+
+
+
+
Name
+
+
{{ errors.name }}
+
Valid!
+
+
+
+
Email
+
+
{{ errors.email }}
+
Valid!
+
+
+
+
Avatar
+
+
{{ errors.avatar }}
+
Valid!
+
+ Files are validated during precognitive requests when validateFiles is enabled
+
+
+
+ Validating...
+
+
+ Submit
+
+ Validate All
+
+
+
+
+
+
+
+
Touch & Reset Methods
+
+
+
+
Name
+
+
{{ errors.name }}
+
Touched: {{ touched('name') }}
+
+
+
+
Email
+
+
{{ errors.email }}
+
Touched: {{ touched('email') }}
+
+
+ Validating...
+
+
+
+ Validate
+
+
+ Reset All
+
+
+ Reset Name
+
+
+
+
+
Status:
+
+ Any field touched: {{ touched() }}
+ Name touched: {{ touched('name') }}
+ Email touched: {{ touched('email') }}
+
+
+
+
+
+
+
+
Validation Callbacks
+
+
+
+
+
Name
+
+
{{ errors.name }}
+
+
+ Validating...
+
+
+
onSuccess called!
+
onError called!
+
onFinish called!
+
+ onException: {{ callbacks.exceptionMessage }}
+
+
+
+
+
+ Validate
+
+
+
+
+
+
+
diff --git a/playgrounds/vue3/routes/web.php b/playgrounds/vue3/routes/web.php
index efa24a69e..fb084bab6 100644
--- a/playgrounds/vue3/routes/web.php
+++ b/playgrounds/vue3/routes/web.php
@@ -1,6 +1,8 @@
validated();
+
+ return back()->with('success', 'Form submitted successfully!');
+})->middleware([HandlePrecognitiveRequests::class]);
Route::post('/user', function () {
return inertia('User', [
'user' => request()->validate([
From 94d83b56c8ed1ac5fbc93ef8a1cfd9e7fe883fa0 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 17:32:44 +0200
Subject: [PATCH 051/145] React playground
---
.../Http/Requests/PrecognitionFormRequest.php | 32 ++
.../react/resources/js/Components/Layout.tsx | 7 +-
.../js/Pages/FormComponentPrecognition.tsx | 290 ++++++++++++++++++
playgrounds/react/routes/web.php | 13 +
4 files changed, 340 insertions(+), 2 deletions(-)
create mode 100644 playgrounds/react/app/Http/Requests/PrecognitionFormRequest.php
create mode 100644 playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
diff --git a/playgrounds/react/app/Http/Requests/PrecognitionFormRequest.php b/playgrounds/react/app/Http/Requests/PrecognitionFormRequest.php
new file mode 100644
index 000000000..3fc8a6916
--- /dev/null
+++ b/playgrounds/react/app/Http/Requests/PrecognitionFormRequest.php
@@ -0,0 +1,32 @@
+|string>
+ */
+ public function rules(): array
+ {
+ sleep(1);
+
+ return [
+ 'name' => ['required', 'string', 'min:3', 'max:255'],
+ 'email' => ['required', 'email', 'max:255'],
+ 'avatar' => ['nullable', 'file', 'image'],
+ ];
+ }
+}
diff --git a/playgrounds/react/resources/js/Components/Layout.tsx b/playgrounds/react/resources/js/Components/Layout.tsx
index 69d7cb70b..b1a0abc6d 100644
--- a/playgrounds/react/resources/js/Components/Layout.tsx
+++ b/playgrounds/react/resources/js/Components/Layout.tsx
@@ -17,10 +17,13 @@ export default function Layout({ children, padding = true }: { children: React.R
Article
- Form
+ useForm
- Form Component
+ {''}
+
+
+ Precognition
Async
diff --git a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
new file mode 100644
index 000000000..0c626bc63
--- /dev/null
+++ b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
@@ -0,0 +1,290 @@
+import { Form, Head } from '@inertiajs/react'
+import { useState } from 'react'
+import Layout from '../Components/Layout'
+
+const FormComponentPrecognition = () => {
+ const [callbacks, setCallbacks] = useState({
+ success: false,
+ error: false,
+ finish: false,
+ exception: false,
+ exceptionMessage: '',
+ })
+
+ const validateWithCallbacks = (validate) => {
+ setCallbacks({
+ success: false,
+ error: false,
+ finish: false,
+ exception: false,
+ exceptionMessage: '',
+ })
+
+ validate('name', {
+ onSuccess: () => setCallbacks((prev) => ({ ...prev, success: true })),
+ onError: () => setCallbacks((prev) => ({ ...prev, error: true })),
+ onFinish: () => setCallbacks((prev) => ({ ...prev, finish: true })),
+ onBeforeValidation: (newReq, oldReq) => {
+ // Prevent validation if name is 'block'
+ if (newReq.data.name === 'block') {
+ alert('Validation blocked by onBeforeValidation!')
+ return false
+ }
+ },
+ })
+ }
+
+ const [validateFiles, setValidateFiles] = useState(false)
+ const [validateTimeout, setValidateTimeout] = useState(1500)
+
+ return (
+ <>
+
+ Form Precognition
+
+ {/* Live Validation & File Uploads */}
+
+
+
Live Validation & File Uploads
+
+ {/* Configuration Toggle */}
+
+
+ setValidateFiles(e.target.checked)}
+ className="rounded"
+ />
+ Enable file validation
+
+
+ Timeout:
+ setValidateTimeout(Number(e.target.value))}
+ className="rounded border px-2 py-1 text-sm"
+ >
+ 500ms
+ 1000ms
+ 1500ms
+ 2000ms
+
+
+
+
+
+ {({ errors, invalid, valid, validate, validating }) => (
+ <>
+
+
+ Name
+
+
validate('name')}
+ className={`mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm ${
+ invalid('name') ? 'border-red-500' : valid('name') ? 'border-green-500' : ''
+ }`}
+ />
+ {invalid('name') &&
{errors.name}
}
+ {valid('name') &&
Valid!
}
+
+
+
+
+ Email
+
+
validate('email')}
+ className={`mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm ${
+ invalid('email') ? 'border-red-500' : valid('email') ? 'border-green-500' : ''
+ }`}
+ />
+ {invalid('email') &&
{errors.email}
}
+ {valid('email') &&
Valid!
}
+
+
+
+
+ Avatar
+
+
validate('avatar')}
+ className={`mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm ${
+ invalid('avatar') ? 'border-red-500' : valid('avatar') ? 'border-green-500' : ''
+ }`}
+ />
+ {invalid('avatar') &&
{errors.avatar}
}
+ {valid('avatar') &&
Valid!
}
+
+ Files are validated during precognitive requests when validateFiles is enabled
+
+
+
+ {validating && Validating...
}
+
+
+
+ Submit
+
+ validate(['name', 'email', ...(validateFiles ? ['avatar'] : [])])}
+ className="rounded bg-blue-600 px-4 py-2 text-white"
+ >
+ Validate All
+
+
+ >
+ )}
+
+
+
+ {/* Touch & Reset Methods */}
+
+
Touch & Reset Methods
+
+
+ {({ errors, invalid, validate, touch, touched, reset, validating }) => (
+ <>
+
+
+ Name
+
+
touch('name')}
+ className="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {invalid('name') &&
{errors.name}
}
+
Touched: {String(touched('name'))}
+
+
+
+
+ Email
+
+
touch('email')}
+ className="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {invalid('email') &&
{errors.email}
}
+
Touched: {String(touched('email'))}
+
+
+ {validating && Validating...
}
+
+
+ validate()}
+ className="rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
+ >
+ Validate
+
+ reset()}
+ className="rounded bg-gray-600 px-3 py-1.5 text-sm text-white"
+ >
+ Reset All
+
+ reset('name')}
+ className="rounded bg-gray-600 px-3 py-1.5 text-sm text-white"
+ >
+ Reset Name
+
+
+
+
+
Status:
+
+ Any field touched: {String(touched())}
+ Name touched: {String(touched('name'))}
+ Email touched: {String(touched('email'))}
+
+
+ >
+ )}
+
+
+
+ {/* Validation Callbacks */}
+
+
Validation Callbacks
+
+
+ {({ errors, invalid, validate, touch, validating }) => (
+ <>
+
+
+ Name
+
+
touch('name')}
+ className="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {invalid('name') &&
{errors.name}
}
+
+
+ {validating && Validating...
}
+
+ {(callbacks.success || callbacks.error || callbacks.finish) && (
+
+ {callbacks.success &&
onSuccess called!
}
+ {callbacks.error &&
onError called!
}
+ {callbacks.finish &&
onFinish called!
}
+ {callbacks.exception && (
+
onException: {callbacks.exceptionMessage}
+ )}
+
+ )}
+
+
+ validateWithCallbacks(validate)}
+ className="rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
+ >
+ Validate
+
+
+ >
+ )}
+
+
+
+ >
+ )
+}
+
+FormComponentPrecognition.layout = (page) =>
+
+export default FormComponentPrecognition
diff --git a/playgrounds/react/routes/web.php b/playgrounds/react/routes/web.php
index 61aa69c20..1a151811c 100644
--- a/playgrounds/react/routes/web.php
+++ b/playgrounds/react/routes/web.php
@@ -1,6 +1,9 @@
validated();
+
+ return back()->with('success', 'Form submitted successfully!');
+})->middleware([HandlePrecognitiveRequests::class]);
+
Route::post('/user', function () {
return inertia('User', [
'user' => request()->validate([
From 29f49eb3da8ef2da5efffc61ee3ac41eb516c858 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 17:35:31 +0200
Subject: [PATCH 052/145] Svelte 4 playground
---
.../Http/Requests/PrecognitionFormRequest.php | 32 ++
.../resources/js/Components/Layout.svelte | 5 +-
.../js/Pages/FormComponentPrecognition.svelte | 297 ++++++++++++++++++
playgrounds/svelte4/routes/web.php | 12 +
4 files changed, 344 insertions(+), 2 deletions(-)
create mode 100644 playgrounds/svelte4/app/Http/Requests/PrecognitionFormRequest.php
create mode 100644 playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
diff --git a/playgrounds/svelte4/app/Http/Requests/PrecognitionFormRequest.php b/playgrounds/svelte4/app/Http/Requests/PrecognitionFormRequest.php
new file mode 100644
index 000000000..3fc8a6916
--- /dev/null
+++ b/playgrounds/svelte4/app/Http/Requests/PrecognitionFormRequest.php
@@ -0,0 +1,32 @@
+|string>
+ */
+ public function rules(): array
+ {
+ sleep(1);
+
+ return [
+ 'name' => ['required', 'string', 'min:3', 'max:255'],
+ 'email' => ['required', 'email', 'max:255'],
+ 'avatar' => ['nullable', 'file', 'image'],
+ ];
+ }
+}
diff --git a/playgrounds/svelte4/resources/js/Components/Layout.svelte b/playgrounds/svelte4/resources/js/Components/Layout.svelte
index e9e358dbf..02a2cd9b9 100644
--- a/playgrounds/svelte4/resources/js/Components/Layout.svelte
+++ b/playgrounds/svelte4/resources/js/Components/Layout.svelte
@@ -12,8 +12,9 @@
Home
Users
Article
- Form
- Form Component
+ useForm
+ {''}
+ Precognition
Logout
External
Async
diff --git a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
new file mode 100644
index 000000000..29b363f3d
--- /dev/null
+++ b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
@@ -0,0 +1,297 @@
+
+
+
+
+
+ Precognition - {appName}
+
+
+Form Precognition
+
+
+
+
+
Live Validation & File Uploads
+
+
+
+
+
+ Enable file validation
+
+
+ Timeout:
+
+ 500ms
+ 1000ms
+ 1500ms
+ 2000ms
+
+
+
+
+
+
+
Name
+
validate('name')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ class:border-red-500={invalid('name')}
+ class:border-green-500={valid('name')}
+ />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+ {#if valid('name')}
+
Valid!
+ {/if}
+
+
+
+
Email
+
validate('email')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ class:border-red-500={invalid('email')}
+ class:border-green-500={valid('email')}
+ />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+ {#if valid('email')}
+
Valid!
+ {/if}
+
+
+
+
Avatar
+
validate('avatar')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ class:border-red-500={invalid('avatar')}
+ class:border-green-500={valid('avatar')}
+ />
+ {#if invalid('avatar')}
+
{errors.avatar}
+ {/if}
+ {#if valid('avatar')}
+
Valid!
+ {/if}
+
+ Files are validated during precognitive requests when validateFiles is enabled
+
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+ Submit
+ validate(['name', 'email', ...(validateFiles ? ['avatar'] : [])])}
+ class="rounded bg-blue-600 px-4 py-2 text-white"
+ >
+ Validate All
+
+
+
+
+
+
+
+
Touch & Reset Methods
+
+
+
+
Name
+
touch('name')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
Touched: {touched('name')}
+
+
+
+
Email
+
touch('email')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+
Touched: {touched('email')}
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+ validate()} class="rounded bg-blue-600 px-3 py-1.5 text-sm text-white">
+ Validate
+
+ reset()} class="rounded bg-gray-600 px-3 py-1.5 text-sm text-white">
+ Reset All
+
+ reset('name')} class="rounded bg-gray-600 px-3 py-1.5 text-sm text-white">
+ Reset Name
+
+
+
+
+
Status:
+
+ Any field touched: {touched()}
+ Name touched: {touched('name')}
+ Email touched: {touched('email')}
+
+
+
+
+
+
+
+
Validation Callbacks
+
+
+
+
Name
+
touch('name')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+ {#if callbacks.success || callbacks.error || callbacks.finish}
+
+ {#if callbacks.success}
+
onSuccess called!
+ {/if}
+ {#if callbacks.error}
+
onError called!
+ {/if}
+ {#if callbacks.finish}
+
onFinish called!
+ {/if}
+ {#if callbacks.exception}
+
onException: {callbacks.exceptionMessage}
+ {/if}
+
+ {/if}
+
+
+ validateWithCallbacks(validate)}
+ class="rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
+ >
+ Validate
+
+
+
+
+
diff --git a/playgrounds/svelte4/routes/web.php b/playgrounds/svelte4/routes/web.php
index b8029f3ec..eea1c5cf1 100644
--- a/playgrounds/svelte4/routes/web.php
+++ b/playgrounds/svelte4/routes/web.php
@@ -1,5 +1,7 @@
validated();
+
+ return back()->with('success', 'Form submitted successfully!');
+})->middleware([HandlePrecognitiveRequests::class]);
+
Route::get('/photo-grid/{horizontal?}', function ($horizontal = null) {
if (request()->header('X-Inertia-Partial-Component')) {
// Simulate latency for partial reloads
From ddd58e25cc1a705c4d8fb291c42a3f8d906e1075 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 17:37:44 +0200
Subject: [PATCH 053/145] Svelte 5 playground
---
.../Http/Requests/PrecognitionFormRequest.php | 32 ++
.../resources/js/Components/Layout.svelte | 5 +-
.../js/Pages/FormComponentPrecognition.svelte | 282 ++++++++++++++++++
playgrounds/svelte5/routes/web.php | 12 +
4 files changed, 329 insertions(+), 2 deletions(-)
create mode 100644 playgrounds/svelte5/app/Http/Requests/PrecognitionFormRequest.php
create mode 100644 playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
diff --git a/playgrounds/svelte5/app/Http/Requests/PrecognitionFormRequest.php b/playgrounds/svelte5/app/Http/Requests/PrecognitionFormRequest.php
new file mode 100644
index 000000000..3fc8a6916
--- /dev/null
+++ b/playgrounds/svelte5/app/Http/Requests/PrecognitionFormRequest.php
@@ -0,0 +1,32 @@
+|string>
+ */
+ public function rules(): array
+ {
+ sleep(1);
+
+ return [
+ 'name' => ['required', 'string', 'min:3', 'max:255'],
+ 'email' => ['required', 'email', 'max:255'],
+ 'avatar' => ['nullable', 'file', 'image'],
+ ];
+ }
+}
diff --git a/playgrounds/svelte5/resources/js/Components/Layout.svelte b/playgrounds/svelte5/resources/js/Components/Layout.svelte
index f5ae9ec6d..a66a99102 100644
--- a/playgrounds/svelte5/resources/js/Components/Layout.svelte
+++ b/playgrounds/svelte5/resources/js/Components/Layout.svelte
@@ -9,8 +9,9 @@
Home
Users
Article
- Form
- Form Component
+ useForm
+ {''}
+ Precognition
Photo Grid
Photo Row
Data Table
diff --git a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
new file mode 100644
index 000000000..5c76e9ee9
--- /dev/null
+++ b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
@@ -0,0 +1,282 @@
+
+
+
+
+
+ Precognition - {appName}
+
+
+Form Precognition
+
+
+
+
+
Live Validation & File Uploads
+
+
+
+
+
+ Enable file validation
+
+
+ Timeout:
+
+ 500ms
+ 1000ms
+ 1500ms
+ 2000ms
+
+
+
+
+
+ {#snippet children({ errors, invalid, valid, validate, validating })}
+
+
Name
+
validate('name')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ class:border-red-500={invalid('name')}
+ class:border-green-500={valid('name')}
+ />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+ {#if valid('name')}
+
Valid!
+ {/if}
+
+
+
+
Email
+
validate('email')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ class:border-red-500={invalid('email')}
+ class:border-green-500={valid('email')}
+ />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+ {#if valid('email')}
+
Valid!
+ {/if}
+
+
+
+
Avatar
+
validate('avatar')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ class:border-red-500={invalid('avatar')}
+ class:border-green-500={valid('avatar')}
+ />
+ {#if invalid('avatar')}
+
{errors.avatar}
+ {/if}
+ {#if valid('avatar')}
+
Valid!
+ {/if}
+
+ Files are validated during precognitive requests when validateFiles is enabled
+
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+ Submit
+ validate(['name', 'email', ...(validateFiles ? ['avatar'] : [])])}
+ class="rounded bg-blue-600 px-4 py-2 text-white"
+ >
+ Validate All
+
+
+ {/snippet}
+
+
+
+
+
+
Touch & Reset Methods
+
+
+ {#snippet children({ errors, invalid, validate, touch, touched, reset, validating })}
+
+
Name
+
touch('name')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
Touched: {touched('name')}
+
+
+
+
Email
+
touch('email')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {#if invalid('email')}
+
{errors.email}
+ {/if}
+
Touched: {touched('email')}
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+
+ validate()} class="rounded bg-blue-600 px-3 py-1.5 text-sm text-white">
+ Validate
+
+ reset()} class="rounded bg-gray-600 px-3 py-1.5 text-sm text-white">
+ Reset All
+
+ reset('name')}
+ class="rounded bg-gray-600 px-3 py-1.5 text-sm text-white"
+ >
+ Reset Name
+
+
+
+
+
Status:
+
+ Any field touched: {touched()}
+ Name touched: {touched('name')}
+ Email touched: {touched('email')}
+
+
+ {/snippet}
+
+
+
+
+
+
Validation Callbacks
+
+
+ {#snippet children({ errors, invalid, validate, touch, validating })}
+
+
Name
+
touch('name')}
+ class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
+ />
+ {#if invalid('name')}
+
{errors.name}
+ {/if}
+
+
+ {#if validating}
+ Validating...
+ {/if}
+
+ {#if callbacks.success || callbacks.error || callbacks.finish}
+
+ {#if callbacks.success}
+
onSuccess called!
+ {/if}
+ {#if callbacks.error}
+
onError called!
+ {/if}
+ {#if callbacks.finish}
+
onFinish called!
+ {/if}
+ {#if callbacks.exception}
+
onException: {callbacks.exceptionMessage}
+ {/if}
+
+ {/if}
+
+
+ validateWithCallbacks(validate)}
+ class="rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
+ >
+ Validate
+
+
+ {/snippet}
+
+
+
diff --git a/playgrounds/svelte5/routes/web.php b/playgrounds/svelte5/routes/web.php
index 29df038d2..344149fce 100644
--- a/playgrounds/svelte5/routes/web.php
+++ b/playgrounds/svelte5/routes/web.php
@@ -1,5 +1,7 @@
validated();
+
+ return back()->with('success', 'Form submitted successfully!');
+})->middleware([HandlePrecognitiveRequests::class]);
+
Route::get('/photo-grid/{horizontal?}', function ($horizontal = null) {
if (request()->header('X-Inertia-Partial-Component')) {
// Simulate latency for partial reloads
From 3298c421ed8415ce7c3dd2f8601dc77503e0502c Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 14 Oct 2025 17:41:05 +0200
Subject: [PATCH 054/145] Make Prettier happier
---
.../resources/js/Pages/FormComponentPrecognition.svelte | 8 +-------
playgrounds/vue3/resources/js/Components/Layout.vue | 2 +-
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
index 5c76e9ee9..c97ecf19e 100644
--- a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
@@ -70,13 +70,7 @@
-
+
{#snippet children({ errors, invalid, valid, validate, validating })}
Name
diff --git a/playgrounds/vue3/resources/js/Components/Layout.vue b/playgrounds/vue3/resources/js/Components/Layout.vue
index a3f07acb7..8da94e29a 100644
--- a/playgrounds/vue3/resources/js/Components/Layout.vue
+++ b/playgrounds/vue3/resources/js/Components/Layout.vue
@@ -23,7 +23,7 @@ const appName = computed(() => page.props.appName)
Users
Article
useForm
-
{{ '
' }}
+ {{ '<' + 'Form' + '>' }}
Precognition
Logout
External
From be8d679c55de788f6b4209ce0bf0b56959493839 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Wed, 15 Oct 2025 15:55:42 +0200
Subject: [PATCH 055/145] Renamed `onBeforeValidation` to `onBefore`
---
packages/core/src/precognition.ts | 4 ++--
packages/core/src/types.ts | 2 +-
packages/react/src/Form.ts | 2 +-
.../Pages/FormComponent/PrecognitionBeforeValidation.tsx | 6 +++---
packages/svelte/src/components/Form.svelte | 2 +-
.../Pages/FormComponent/PrecognitionBeforeValidation.svelte | 4 ++--
packages/vue3/src/form.ts | 2 +-
.../Pages/FormComponent/PrecognitionBeforeValidation.vue | 4 ++--
.../react/resources/js/Pages/FormComponentPrecognition.tsx | 4 ++--
.../resources/js/Pages/FormComponentPrecognition.svelte | 4 ++--
.../resources/js/Pages/FormComponentPrecognition.svelte | 4 ++--
.../vue3/resources/js/Pages/FormComponentPrecognition.vue | 4 ++--
tests/form-component.spec.ts | 2 +-
13 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
index f3865e0f6..c719fe068 100644
--- a/packages/core/src/precognition.ts
+++ b/packages/core/src/precognition.ts
@@ -12,7 +12,7 @@ interface UsePrecognitionOptions {
}
type PrecognitionValidateOptions = Pick, 'method' | 'data' | 'only' | 'errorBag' | 'headers'> &
- Pick & {
+ Pick & {
url: string
onPrecognitionSuccess: () => void
onValidationError: (errors: Errors) => void
@@ -72,7 +72,7 @@ export default function usePrecognition(precognitionOptions: UsePrecognitionOpti
return
}
- const beforeValidatonResult = options.onBeforeValidation?.(
+ const beforeValidatonResult = options.onBefore?.(
{ data: options.data, touched: options.only },
{ data: oldData, touched: oldTouched },
)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index aad328027..14cd54da1 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -489,10 +489,10 @@ type RevalidatePayload = {
export type FormComponentValidateOptions = {
only?: string | string[]
+ onBefore?: (newRequest: RevalidatePayload, oldRequest: RevalidatePayload) => boolean | undefined
onSuccess?: () => void
onError?: (errors: Errors) => void
onFinish?: () => void
- onBeforeValidation?: (newRequest: RevalidatePayload, oldRequest: RevalidatePayload) => boolean | undefined
onException?: (error: Error) => void
}
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index f57c56ff0..d1b1e2522 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -251,7 +251,7 @@ const Form = forwardRef(
errorBag,
headers,
simpleValidationErrors,
- onBeforeValidation: options.onBeforeValidation,
+ onBefore: options.onBefore,
onPrecognitionSuccess: () => {
setValidated((prev) => [...prev, ...fields])
form.clearErrors(...fields)
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
index 778b7de4d..bbb2bd98d 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
@@ -1,7 +1,7 @@
import { Form } from '@inertiajs/react'
import { isEqual } from 'lodash-es'
-export default function PrecognitionBeforeValidation() {
+export default function PrecognitionBefore() {
const handleBeforeValidation = (
newRequest: { data: Record; touched: string[] },
oldRequest: { data: Record; touched: string[] },
@@ -19,7 +19,7 @@ export default function PrecognitionBeforeValidation() {
return (
-
Precognition - onBeforeValidation
+
Precognition - onBefore
{({ errors, invalid, validate, validating }) => (
@@ -31,7 +31,7 @@ export default function PrecognitionBeforeValidation() {
name="name"
onChange={(e) => {
validate('name', {
- onBeforeValidation: handleBeforeValidation,
+ onBefore: handleBeforeValidation,
})
}}
/>
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 8958b1031..bf0702d01 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -232,7 +232,7 @@
errorBag,
headers,
simpleValidationErrors,
- onBeforeValidation: options.onBeforeValidation,
+ onBefore: options.onBefore,
onPrecognitionSuccess: () => {
validated = [...validated, ...fields]
clearErrors(...fields)
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
index 39544007e..337b9dae8 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
@@ -20,7 +20,7 @@
-
Precognition - onBeforeValidation
+
Precognition - onBefore
validate('name', {
- onBeforeValidation: handleBeforeValidation,
+ onBefore: handleBeforeValidation,
})}
/>
{#if invalid('name')}
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 0978df16e..b5bc68f45 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -301,7 +301,7 @@ const Form = defineComponent({
errorBag: props.errorBag,
headers: props.headers,
simpleValidationErrors: props.simpleValidationErrors,
- onBeforeValidation: options.onBeforeValidation,
+ onBefore: options.onBefore,
onPrecognitionSuccess: () => {
validated.value = [...validated.value, ...fields]
form.clearErrors(...fields)
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
index 5a25b9f51..e55e96a1b 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
@@ -20,7 +20,7 @@ const handleBeforeValidation = (
-
Precognition - onBeforeValidation
+
Precognition - onBefore
diff --git a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
index 0c626bc63..026b74ea5 100644
--- a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
+++ b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
@@ -24,10 +24,10 @@ const FormComponentPrecognition = () => {
onSuccess: () => setCallbacks((prev) => ({ ...prev, success: true })),
onError: () => setCallbacks((prev) => ({ ...prev, error: true })),
onFinish: () => setCallbacks((prev) => ({ ...prev, finish: true })),
- onBeforeValidation: (newReq, oldReq) => {
+ onBefore: (newReq, oldReq) => {
// Prevent validation if name is 'block'
if (newReq.data.name === 'block') {
- alert('Validation blocked by onBeforeValidation!')
+ alert('Validation blocked by onBefore!')
return false
}
},
diff --git a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
index 29b363f3d..95eea30f9 100644
--- a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
@@ -28,10 +28,10 @@
onSuccess: () => (callbacks.success = true),
onError: () => (callbacks.error = true),
onFinish: () => (callbacks.finish = true),
- onBeforeValidation: (newReq, oldReq) => {
+ onBefore: (newReq, oldReq) => {
// Prevent validation if name is 'block'
if (newReq.data.name === 'block') {
- alert('Validation blocked by onBeforeValidation!')
+ alert('Validation blocked by onBefore!')
return false
}
},
diff --git a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
index c97ecf19e..043a25b71 100644
--- a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
@@ -28,10 +28,10 @@
onSuccess: () => (callbacks.success = true),
onError: () => (callbacks.error = true),
onFinish: () => (callbacks.finish = true),
- onBeforeValidation: (newReq, oldReq) => {
+ onBefore: (newReq, oldReq) => {
// Prevent validation if name is 'block'
if (newReq.data.name === 'block') {
- alert('Validation blocked by onBeforeValidation!')
+ alert('Validation blocked by onBefore!')
return false
}
},
diff --git a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
index 529c0b005..d1d494120 100644
--- a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
+++ b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
@@ -28,10 +28,10 @@ const validateWithCallbacks = (validate) => {
onSuccess: () => (callbacks.value.success = true),
onError: () => (callbacks.value.error = true),
onFinish: () => (callbacks.value.finish = true),
- onBeforeValidation: (newReq, oldReq) => {
+ onBefore: (newReq, oldReq) => {
// Prevent validation if name is 'block'
if (newReq.data.name === 'block') {
- alert('Validation blocked by onBeforeValidation!')
+ alert('Validation blocked by onBefore!')
return false
}
},
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 78a26661e..9d258e21d 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1911,7 +1911,7 @@ test.describe('Form Component', () => {
await expect(page.getByText('onFinish called!')).toBeVisible()
})
- test('onBeforeValidation can block validation', async ({ page }) => {
+ test('onBefore can block validation', async ({ page }) => {
await page.goto('/form-component/precognition-before-validation')
await page.fill('input[name="name"]', 'block')
From ae1d2ceb37acd9632d9e2fd21fdcce89e1d4ca14 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Wed, 15 Oct 2025 17:38:08 +0200
Subject: [PATCH 056/145] Improve playground
---
.../js/Pages/FormComponentPrecognition.tsx | 21 ++++---------------
playgrounds/react/routes/web.php | 4 +++-
.../js/Pages/FormComponentPrecognition.svelte | 21 ++++---------------
playgrounds/svelte4/routes/web.php | 4 +++-
.../js/Pages/FormComponentPrecognition.svelte | 21 ++++---------------
playgrounds/svelte5/routes/web.php | 4 +++-
.../js/Pages/FormComponentPrecognition.vue | 19 ++++-------------
playgrounds/vue3/routes/web.php | 6 ++++--
8 files changed, 29 insertions(+), 71 deletions(-)
diff --git a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
index 026b74ea5..d0388b83a 100644
--- a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
+++ b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
@@ -82,6 +82,8 @@ const FormComponentPrecognition = () => {
>
{({ errors, invalid, valid, validate, validating }) => (
<>
+ Validating: {validating ? ' Yes...' : ' No'}
+
Name
@@ -90,7 +92,7 @@ const FormComponentPrecognition = () => {
id="name"
name="name"
placeholder="Enter your name (min 3 chars)"
- onChange={() => validate('name')}
+ onBlur={() => validate('name')}
className={`mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm ${
invalid('name') ? 'border-red-500' : valid('name') ? 'border-green-500' : ''
}`}
@@ -108,7 +110,7 @@ const FormComponentPrecognition = () => {
name="email"
type="email"
placeholder="Enter your email"
- onChange={() => validate('email')}
+ onBlur={() => validate('email')}
className={`mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm ${
invalid('email') ? 'border-red-500' : valid('email') ? 'border-green-500' : ''
}`}
@@ -136,21 +138,6 @@ const FormComponentPrecognition = () => {
Files are validated during precognitive requests when validateFiles is enabled
-
- {validating && Validating...
}
-
-
-
- Submit
-
- validate(['name', 'email', ...(validateFiles ? ['avatar'] : [])])}
- className="rounded bg-blue-600 px-4 py-2 text-white"
- >
- Validate All
-
-
>
)}
diff --git a/playgrounds/react/routes/web.php b/playgrounds/react/routes/web.php
index 1a151811c..36db775ce 100644
--- a/playgrounds/react/routes/web.php
+++ b/playgrounds/react/routes/web.php
@@ -119,7 +119,9 @@
Route::post('/form-component/precognition', function (PrecognitionFormRequest $request) {
$data = $request->validated();
- return back()->with('success', 'Form submitted successfully!');
+ // dd($data);
+
+ return back();
})->middleware([HandlePrecognitiveRequests::class]);
Route::post('/user', function () {
diff --git a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
index 95eea30f9..44b70730c 100644
--- a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
@@ -82,13 +82,15 @@
let:validate
let:validating
>
+ Validating: {validating ? 'Yes...' : 'No'}
+
Name
validate('name')}
+ on:blur={() => validate('name')}
class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
class:border-red-500={invalid('name')}
class:border-green-500={valid('name')}
@@ -108,7 +110,7 @@
name="email"
type="email"
placeholder="Enter your email"
- on:change={() => validate('email')}
+ on:blur={() => validate('email')}
class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
class:border-red-500={invalid('email')}
class:border-green-500={valid('email')}
@@ -142,21 +144,6 @@
Files are validated during precognitive requests when validateFiles is enabled
-
- {#if validating}
- Validating...
- {/if}
-
-
- Submit
- validate(['name', 'email', ...(validateFiles ? ['avatar'] : [])])}
- class="rounded bg-blue-600 px-4 py-2 text-white"
- >
- Validate All
-
-
diff --git a/playgrounds/svelte4/routes/web.php b/playgrounds/svelte4/routes/web.php
index eea1c5cf1..0dec64e82 100644
--- a/playgrounds/svelte4/routes/web.php
+++ b/playgrounds/svelte4/routes/web.php
@@ -406,7 +406,9 @@ function () use ($start, $end, $itemType) {
Route::post('/form-component/precognition', function (PrecognitionFormRequest $request) {
$data = $request->validated();
- return back()->with('success', 'Form submitted successfully!');
+ // dd($data);
+
+ return back();
})->middleware([HandlePrecognitiveRequests::class]);
Route::get('/photo-grid/{horizontal?}', function ($horizontal = null) {
diff --git a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
index 043a25b71..30b2ade76 100644
--- a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
@@ -72,13 +72,15 @@
{#snippet children({ errors, invalid, valid, validate, validating })}
+ Validating: {validating ? 'Yes...' : 'No'}
+
Name
validate('name')}
+ onblur={() => validate('name')}
class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
class:border-red-500={invalid('name')}
class:border-green-500={valid('name')}
@@ -98,7 +100,7 @@
name="email"
type="email"
placeholder="Enter your email"
- onchange={() => validate('email')}
+ onblur={() => validate('email')}
class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
class:border-red-500={invalid('email')}
class:border-green-500={valid('email')}
@@ -132,21 +134,6 @@
Files are validated during precognitive requests when validateFiles is enabled
-
- {#if validating}
- Validating...
- {/if}
-
-
- Submit
- validate(['name', 'email', ...(validateFiles ? ['avatar'] : [])])}
- class="rounded bg-blue-600 px-4 py-2 text-white"
- >
- Validate All
-
-
{/snippet}
diff --git a/playgrounds/svelte5/routes/web.php b/playgrounds/svelte5/routes/web.php
index 344149fce..28c3938ca 100644
--- a/playgrounds/svelte5/routes/web.php
+++ b/playgrounds/svelte5/routes/web.php
@@ -406,7 +406,9 @@ function () use ($start, $end, $itemType) {
Route::post('/form-component/precognition', function (PrecognitionFormRequest $request) {
$data = $request->validated();
- return back()->with('success', 'Form submitted successfully!');
+ // dd($data);
+
+ return back();
})->middleware([HandlePrecognitiveRequests::class]);
Route::get('/photo-grid/{horizontal?}', function ($horizontal = null) {
diff --git a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
index d1d494120..384fc0b71 100644
--- a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
+++ b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
@@ -76,13 +76,15 @@ const validateTimeout = ref(1500)
#default="{ errors, invalid, valid, validate, validating }"
class="space-y-4"
>
+ Validating: {{ validating ? 'Yes...' : 'No' }}
+
Name
@@ -97,7 +99,7 @@ const validateTimeout = ref(1500)
name="email"
type="email"
placeholder="Enter your email"
- @change="validate('email')"
+ @blur="validate('email')"
class="mt-1 w-full appearance-none rounded border px-2 py-1 shadow-sm"
:class="invalid('email') ? 'border-red-500' : valid('email') ? 'border-green-500' : ''"
/>
@@ -121,19 +123,6 @@ const validateTimeout = ref(1500)
Files are validated during precognitive requests when validateFiles is enabled
-
- Validating...
-
-
- Submit
-
- Validate All
-
-
diff --git a/playgrounds/vue3/routes/web.php b/playgrounds/vue3/routes/web.php
index fb084bab6..11af53004 100644
--- a/playgrounds/vue3/routes/web.php
+++ b/playgrounds/vue3/routes/web.php
@@ -171,11 +171,13 @@
});
Route::post('/form-component/precognition', function (PrecognitionFormRequest $request) {
-
$data = $request->validated();
- return back()->with('success', 'Form submitted successfully!');
+ // dd($data);
+
+ return back();
})->middleware([HandlePrecognitiveRequests::class]);
+
Route::post('/user', function () {
return inertia('User', [
'user' => request()->validate([
From 192f981bbc5da0a6bcc1d3224fe2365fcbe615b5 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 12:11:45 +0100
Subject: [PATCH 057/145] Build upon `laravel-precognition` library
---
package.json | 4 +-
packages/core/package.json | 11 +-
packages/core/src/index.ts | 1 -
packages/core/src/precognition.ts | 151 --
packages/core/src/types.ts | 21 +-
packages/react/package.json | 15 +-
packages/react/src/Form.ts | 4 -
.../FormComponent/PrecognitionCallbacks.tsx | 38 +-
.../FormComponent/PrecognitionCancel.tsx | 20 -
packages/react/test-app/package.json | 18 +-
packages/svelte/package.json | 13 +-
packages/svelte/src/components/Form.svelte | 5 -
.../PrecognitionCallbacks.svelte | 35 +-
.../FormComponent/PrecognitionCancel.svelte | 23 -
packages/svelte/test-app/package.json | 12 +-
packages/vue3/package.json | 13 +-
packages/vue3/src/form.ts | 175 +-
.../FormComponent/PrecognitionCallbacks.vue | 30 +-
.../FormComponent/PrecognitionCancel.vue | 19 -
packages/vue3/test-app/package.json | 12 +-
playgrounds/react/package.json | 20 +-
playgrounds/svelte4/package.json | 12 +-
playgrounds/svelte5/package.json | 14 +-
playgrounds/vue3/package.json | 18 +-
pnpm-lock.yaml | 1936 ++++++++---------
tests/app/package.json | 10 +-
tests/form-component.spec.ts | 54 +-
27 files changed, 1106 insertions(+), 1578 deletions(-)
delete mode 100644 packages/core/src/precognition.ts
diff --git a/package.json b/package.json
index 0d89b3fee..372ed52a5 100644
--- a/package.json
+++ b/package.json
@@ -28,13 +28,13 @@
"format": "prettier --write ."
},
"dependencies": {
- "@playwright/test": "^1.55.1",
+ "@playwright/test": "^1.56.1",
"prettier": "^3.6.2",
"prettier-plugin-organize-imports": "^4.3.0",
"prettier-plugin-svelte": "^3.4.0",
"prettier-plugin-tailwindcss": "^0.6.14"
},
"optionalDependencies": {
- "@rollup/rollup-linux-x64-gnu": "^4.52.2"
+ "@rollup/rollup-linux-x64-gnu": "^4.52.5"
}
}
diff --git a/packages/core/package.json b/packages/core/package.json
index 855194e14..4e5801e20 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -54,18 +54,19 @@
},
"dependencies": {
"@types/lodash-es": "^4.17.12",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"lodash-es": "^4.17.21",
"qs": "^6.14.0"
},
"devDependencies": {
"@types/deepmerge": "^2.2.3",
- "@types/node": "^18.19.127",
+ "@types/node": "^18.19.130",
"@types/nprogress": "^0.2.3",
"@types/qs": "^6.14.0",
- "es-check": "^9.3.1",
- "esbuild": "^0.25.10",
+ "es-check": "^9.4.4",
+ "esbuild": "^0.25.11",
"esbuild-node-externals": "^1.18.0",
- "typescript": "^5.9.2"
+ "laravel-precognition": "^0.7.3",
+ "typescript": "^5.9.3"
}
}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 5ac463f59..30c843cf9 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -7,7 +7,6 @@ export { formDataToObject } from './formObject'
export { default as createHeadManager } from './head'
export { default as useInfiniteScroll } from './infiniteScroll'
export { shouldIntercept, shouldNavigate } from './navigationEvents'
-export { default as usePrecognition } from './precognition'
export { hide as hideProgress, progress, reveal as revealProgress, default as setupProgress } from './progress'
export { resetFormFields } from './resetFormFields'
export * from './types'
diff --git a/packages/core/src/precognition.ts b/packages/core/src/precognition.ts
deleted file mode 100644
index c719fe068..000000000
--- a/packages/core/src/precognition.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-import { default as axios, AxiosRequestConfig } from 'axios'
-import { get, isEqual } from 'lodash-es'
-import debounce from './debounce'
-import { forgetFiles, hasFiles } from './files'
-import { objectToFormData } from './formData'
-import { Errors, FormComponentValidateOptions, RequestData, Visit } from './types'
-
-interface UsePrecognitionOptions {
- timeout: number
- onStart: () => void
- onFinish: () => void
-}
-
-type PrecognitionValidateOptions = Pick, 'method' | 'data' | 'only' | 'errorBag' | 'headers'> &
- Pick & {
- url: string
- onPrecognitionSuccess: () => void
- onValidationError: (errors: Errors) => void
- simpleValidationErrors?: boolean
- }
-
-interface PrecognitionValidator {
- setOldData: (data: RequestData) => void
- validateFiles: (value: boolean) => void
- validate: (options: PrecognitionValidateOptions) => void
- setTimeout: (value: number) => void
- cancelAll: () => void
-}
-
-export default function usePrecognition(precognitionOptions: UsePrecognitionOptions): PrecognitionValidator {
- let debounceTimeoutDuration = precognitionOptions.timeout
- let validateFiles: boolean = false
-
- let oldData: RequestData = {}
- let oldTouched: string[] = []
- const abortControllers: Record = {}
-
- const cancelAll = () => {
- Object.entries(abortControllers).forEach(([key, controller]) => {
- controller.abort()
- delete abortControllers[key]
- })
- }
-
- const setTimeout = (value: number) => {
- if (value !== debounceTimeoutDuration) {
- cancelAll()
- debounceTimeoutDuration = value
- validateFunction = createValidateFunction()
- }
- }
-
- const createFingerprint = (options: PrecognitionValidateOptions) => `${options.method}:${options.url}`
-
- const toSimpleValidationErrors = (errors: Errors): Errors => {
- return Object.keys(errors).reduce(
- (carry, key) => ({
- ...carry,
- [key]: Array.isArray(errors[key]) ? errors[key][0] : errors[key],
- }),
- {},
- )
- }
-
- const createValidateFunction = () =>
- debounce(
- (options: PrecognitionValidateOptions) => {
- const changed = options.only.filter((field) => !isEqual(get(options.data, field), get(oldData, field)))
- const data = validateFiles ? options.data : (forgetFiles(options.data) as RequestData)
-
- if (options.only.length > 0 && changed.length === 0) {
- return
- }
-
- const beforeValidatonResult = options.onBefore?.(
- { data: options.data, touched: options.only },
- { data: oldData, touched: oldTouched },
- )
-
- if (beforeValidatonResult === false) {
- return
- }
-
- const fingerprint = createFingerprint(options)
-
- if (abortControllers[fingerprint]) {
- abortControllers[fingerprint].abort()
- delete abortControllers[fingerprint]
- }
-
- abortControllers[fingerprint] = new AbortController()
-
- precognitionOptions.onStart()
-
- const submitOptions: AxiosRequestConfig = {
- method: options.method,
- url: options.url,
- data: hasFiles(data) ? objectToFormData(data) : { ...data },
- signal: abortControllers[fingerprint].signal,
- headers: {
- ...(options.headers || {}),
- 'X-Requested-With': 'XMLHttpRequest',
- Precognition: true,
- ...(options.only.length ? { 'Precognition-Validate-Only': options.only.join(',') } : {}),
- },
- }
-
- axios(submitOptions)
- .then((response) => {
- if (response.status === 204 && response.headers['precognition-success'] === 'true') {
- options.onPrecognitionSuccess()
- }
- })
- .catch((error) => {
- if (error.response?.status === 422) {
- const errors = error.response.data?.errors || {}
- const scopedErrors = (options.errorBag ? errors[options.errorBag] || {} : errors) as Errors
- const formattedErrors =
- options.simpleValidationErrors === false ? scopedErrors : toSimpleValidationErrors(scopedErrors)
- return options.onValidationError(formattedErrors)
- }
-
- if (options.onException) {
- options.onException(error)
- return
- }
-
- throw error
- })
- .finally(() => {
- oldData = { ...data }
- oldTouched = [...options.only]
- delete abortControllers[fingerprint]
- options.onFinish?.()
- precognitionOptions.onFinish()
- })
- },
- debounceTimeoutDuration,
- { leading: true, trailing: true },
- )
-
- let validateFunction = createValidateFunction()
-
- return {
- setOldData: (data) => (oldData = { ...data }),
- validateFiles: (value) => (validateFiles = value),
- setTimeout,
- validate: (options) => validateFunction(options),
- cancelAll,
- }
-}
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 14cd54da1..35ab2f7e2 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -1,4 +1,5 @@
import { AxiosProgressEvent, AxiosResponse } from 'axios'
+import { Validator } from 'laravel-precognition'
import { Response } from './response'
declare module 'axios' {
@@ -482,20 +483,6 @@ export type FormComponentProps = Partial<
simpleValidationErrors?: boolean
}
-type RevalidatePayload = {
- data: RequestData
- touched: string[]
-}
-
-export type FormComponentValidateOptions = {
- only?: string | string[]
- onBefore?: (newRequest: RevalidatePayload, oldRequest: RevalidatePayload) => boolean | undefined
- onSuccess?: () => void
- onError?: (errors: Errors) => void
- onFinish?: () => void
- onException?: (error: Error) => void
-}
-
export type FormComponentMethods = {
clearErrors: (...fields: string[]) => void
resetAndClearErrors: (...fields: string[]) => void
@@ -506,13 +493,9 @@ export type FormComponentMethods = {
defaults: () => void
valid: (field: string) => boolean
invalid: (field: string) => boolean
- validate: (
- only?: string | string[] | FormComponentValidateOptions,
- maybeOptions?: FormComponentValidateOptions,
- ) => void
+ validate: Validator['validate']
touch: (field: string | string[]) => void
touched(field?: string): boolean
- cancelValidation: () => void
}
export type FormComponentonSubmitCompleteArguments = Pick
diff --git a/packages/react/package.json b/packages/react/package.json
index 7ec40d817..5efbaa77d 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -52,14 +52,14 @@
"es2020-check": "pnpm build:with-deps && es-check es2020 \"dist/index.esm.js\" --checkFeatures --module --noCache --verbose"
},
"devDependencies": {
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
- "axios": "^1.12.2",
- "es-check": "^9.3.1",
- "esbuild": "^0.25.10",
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.2",
+ "axios": "^1.13.1",
+ "es-check": "^9.4.4",
+ "esbuild": "^0.25.11",
"esbuild-node-externals": "^1.18.0",
- "react": "^19.1.1",
- "typescript": "^5.9.2"
+ "react": "^19.2.0",
+ "typescript": "^5.9.3"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -67,6 +67,7 @@
"dependencies": {
"@inertiajs/core": "workspace:*",
"@types/lodash-es": "^4.17.12",
+ "laravel-precognition": "^0.7.3",
"lodash-es": "^4.17.21"
}
}
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index d1b1e2522..555a662cd 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -313,10 +313,6 @@ const Form = forwardRef(
validate,
touch,
touched: isTouched,
- cancelValidation: () => {
- validator.cancelAll()
- setValidating(false)
- },
})
useImperativeHandle(ref, exposed, [
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
index b2e5e583a..370c2eb81 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
@@ -37,8 +37,10 @@ export default () => {
setErrorCalled(false)
setFinishCalled(false)
validate({
- onSuccess: () => {
+ onPrecognitionSuccess: () => {
setSuccessCalled(true)
+ },
+ onFinish: () => {
setFinishCalled(true)
},
})
@@ -54,8 +56,10 @@ export default () => {
setErrorCalled(false)
setFinishCalled(false)
validate({
- onError: () => {
+ onValidationError: () => {
setErrorCalled(true)
+ },
+ onFinish: () => {
setFinishCalled(true)
},
})
@@ -66,36 +70,6 @@ export default () => {
>
)}
-
-
-
-
Exception Test
-
- {({ validate, validating }) => (
- <>
-
-
-
-
- {validating && Validating...
}
- {exceptionCaught && Exception caught: {exceptionMessage}
}
-
- {/* This will trigger a validation request to a non-existent endpoint */}
-
- validate('name', {
- onException: handleException,
- })
- }
- >
- Validate with Exception Handler
-
-
- Submit
- >
- )}
-
)
}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
index 1ea7a108a..5fa49b494 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCancel.tsx
@@ -20,26 +20,6 @@ export default () => {
>
)}
-
-
-
- Manual Cancel Test
-
- {({ validate, cancelValidation, validating }) => (
- <>
-
- validate('name')} />
-
-
- {validating && Validating...
}
-
- cancelValidation()}>
- Cancel Validation
-
- Submit
- >
- )}
-
)
}
diff --git a/packages/react/test-app/package.json b/packages/react/test-app/package.json
index de0d3002c..a3735e9e1 100755
--- a/packages/react/test-app/package.json
+++ b/packages/react/test-app/package.json
@@ -5,18 +5,18 @@
"type-check": "tsc --noEmit"
},
"devDependencies": {
- "@types/node": "^24.1.0",
- "@types/react": "^19.1.9",
- "@types/react-dom": "^19.1.7",
- "nodemon": "^3.0.0",
- "typescript": "^5.9.2",
- "vite": "^6.3.0"
+ "@types/node": "^24.9.2",
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.2",
+ "nodemon": "^3.1.10",
+ "typescript": "^5.9.3",
+ "vite": "^6.4.1"
},
"dependencies": {
"@inertiajs/core": "workspace:*",
"@inertiajs/react": "workspace:*",
- "@vitejs/plugin-react": "^4.3.1",
- "react": "^19.0.0",
- "react-dom": "^19.0.0"
+ "@vitejs/plugin-react": "^4.7.0",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0"
}
}
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 1198a5ad0..646d4a0b7 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -45,17 +45,17 @@
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.3.1",
- "@sveltejs/kit": "^2.43.2",
+ "@sveltejs/kit": "^2.48.3",
"@sveltejs/package": "^2.5.4",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
- "axios": "^1.12.2",
- "es-check": "^9.3.1",
+ "axios": "^1.13.1",
+ "es-check": "^9.4.4",
"publint": "^0.2.12",
"svelte": "^4.2.20",
- "svelte-check": "^4.3.2",
+ "svelte-check": "^4.3.3",
"tslib": "^2.8.1",
- "typescript": "^5.9.2",
- "vite": "^5.4.20"
+ "typescript": "^5.9.3",
+ "vite": "^5.4.21"
},
"peerDependencies": {
"svelte": "^4.0.0 || ^5.0.0"
@@ -63,6 +63,7 @@
"dependencies": {
"@inertiajs/core": "workspace:*",
"@types/lodash-es": "^4.17.12",
+ "laravel-precognition": "^0.7.3",
"lodash-es": "^4.17.21"
}
}
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index bf0702d01..25db4c033 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -282,10 +282,6 @@
return $form.errors[field] !== undefined
}
- export function cancelValidation() {
- validator.cancelAll()
- validating = false
- }
onMount(() => {
defaultData = getFormData()
@@ -343,6 +339,5 @@
touched={touchedMethod}
valid={validMethod}
invalid={invalidMethod}
- {cancelValidation}
/>
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
index 5941c4782..1237f4b37 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
@@ -42,8 +42,10 @@
errorCalled = false
finishCalled = false
validate({
- onSuccess: () => {
+ onPrecognitionSuccess: () => {
successCalled = true
+ },
+ onFinish: () => {
finishCalled = true
},
})
@@ -61,6 +63,8 @@
validate({
onError: () => {
errorCalled = true
+ },
+ onFinish: () => {
finishCalled = true
},
})
@@ -69,33 +73,4 @@
Validate with onError
-
-
-
- Exception Test
-
-
-
-
-
- {#if validating}
- Validating...
- {/if}
- {#if exceptionCaught}
- Exception caught: {exceptionMessage}
- {/if}
-
-
-
- validate('name', {
- onException: handleException,
- })}
- >
- Validate with Exception Handler
-
-
- Submit
-
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
index 4cf3e2ce6..b670c1fe7 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCancel.svelte
@@ -30,27 +30,4 @@
Submit
-
-
-
- Manual Cancel Test
-
-
- validate('name')} />
-
-
- {#if validating}
- Validating...
- {/if}
-
- cancelValidation()}>Cancel Validation
- Submit
-
diff --git a/packages/svelte/test-app/package.json b/packages/svelte/test-app/package.json
index 42cb7e64e..322e595c0 100644
--- a/packages/svelte/test-app/package.json
+++ b/packages/svelte/test-app/package.json
@@ -7,12 +7,12 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.2",
- "@tsconfig/svelte": "^5.0.4",
- "nodemon": "^3.0.0",
- "svelte": "^4.2.14",
- "svelte-check": "^4.1.0",
- "typescript": "^5.9.2",
- "vite": "^5.4.20"
+ "@tsconfig/svelte": "^5.0.5",
+ "nodemon": "^3.1.10",
+ "svelte": "^4.2.20",
+ "svelte-check": "^4.3.3",
+ "typescript": "^5.9.3",
+ "vite": "^5.4.21"
},
"dependencies": {
"@inertiajs/core": "workspace:*",
diff --git a/packages/vue3/package.json b/packages/vue3/package.json
index 06c424c12..35f753e18 100644
--- a/packages/vue3/package.json
+++ b/packages/vue3/package.json
@@ -51,13 +51,13 @@
"es2020-check": "pnpm build:with-deps && es-check es2020 \"dist/index.esm.js\" --checkFeatures --module --noCache --verbose"
},
"devDependencies": {
- "@types/node": "^22.18.6",
- "axios": "^1.12.2",
- "es-check": "^9.3.1",
- "esbuild": "^0.25.10",
+ "@types/node": "^22.18.13",
+ "axios": "^1.13.1",
+ "es-check": "^9.4.4",
+ "esbuild": "^0.25.11",
"esbuild-node-externals": "^1.18.0",
- "typescript": "^5.9.2",
- "vue": "^3.5.21"
+ "typescript": "^5.9.3",
+ "vue": "^3.5.22"
},
"peerDependencies": {
"vue": "^3.0.0"
@@ -65,6 +65,7 @@
"dependencies": {
"@inertiajs/core": "workspace:*",
"@types/lodash-es": "^4.17.12",
+ "laravel-precognition": "^0.7.3",
"lodash-es": "^4.17.21"
}
}
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index b5bc68f45..59aea0934 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -1,17 +1,23 @@
import {
+ Errors,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
- FormComponentValidateOptions,
FormDataConvertible,
formDataToObject,
isUrlMethodPair,
mergeDataIntoQueryString,
Method,
resetFormFields,
- usePrecognition,
VisitOptions,
} from '@inertiajs/core'
+import {
+ createValidator,
+ NamedInputEvent,
+ toSimpleValidationErrors,
+ ValidationConfig,
+ Validator,
+} from 'laravel-precognition'
import { isEqual } from 'lodash-es'
import { computed, defineComponent, h, onBeforeUnmount, onMounted, PropType, ref, SlotsType, watch } from 'vue'
import useForm from './useForm'
@@ -149,31 +155,76 @@ const Form = defineComponent({
const formEvents: Array = ['input', 'change', 'reset']
const validating = ref(false)
- const validated = ref([])
+ const valid = ref([])
const touched = ref([])
- const validator = usePrecognition({
- timeout: props.validateTimeout,
- onStart: () => (validating.value = true),
- onFinish: () => (validating.value = false),
- })
+ let validator: Validator = null
+
+ const clearErrors = (...names: string[]) => {
+ form.clearErrors(...names)
+
+ if (names.length === 0) {
+ validator.setErrors({})
+ } else {
+ names.forEach(validator.forgetError)
+ }
+
+ return form
+ }
+
+ const getTransformedData = (): Record => {
+ const [_url, data] = getUrlAndData()
+
+ return props.transform(data)
+ }
onMounted(() => {
+ validator = createValidator(
+ (client) =>
+ client[method.value](getUrlAndData()[0], getTransformedData(), {
+ headers: props.headers,
+ }),
+ getTransformedData(),
+ )
+ .on('validatingChanged', () => {
+ validating.value = validator.validating()
+ })
+ .on('validatedChanged', () => {
+ valid.value = validator.valid()
+ })
+ .on('touchedChanged', () => {
+ touched.value = validator.touched()
+ })
+ .on('errorsChanged', () => {
+ form.clearErrors()
+
+ form.setError(
+ (props.simpleValidationErrors
+ ? toSimpleValidationErrors(validator.errors())
+ : validator.errors()) as Errors,
+ )
+
+ valid.value = validator.valid()
+ })
+
+ validator.setTimeout(props.validateTimeout)
+
+ if (props.validateFiles) {
+ validator.validateFiles()
+ }
+
defaultData.value = getFormData()
formEvents.forEach((e) => formElement.value.addEventListener(e, onFormUpdate))
- updateDataOnValidator()
})
- watch(
- () => props.validateFiles,
- (value) => validator.validateFiles(value),
- { immediate: true },
- )
+ // watch(
+ // () => props.validateFiles,
+ // (value) => validator.validateFiles(value),
+ // )
watch(
() => props.validateTimeout,
(value) => validator.setTimeout(value),
- { immediate: true },
)
onBeforeUnmount(() => formEvents.forEach((e) => formElement.value?.removeEventListener(e, onFormUpdate)))
@@ -240,97 +291,20 @@ const Form = defineComponent({
form.transform(() => props.transform(data)).submit(method.value, url, submitOptions)
}
- const updateDataOnValidator = () => {
- try {
- // This might fail if the component is already unmounted but this function
- // is called after navigating away after a form submission.
- validator.setOldData(props.transform(getData()))
- } catch {}
- }
-
const reset = (...fields: string[]) => {
resetFormFields(formElement.value, defaultData.value, fields)
- updateDataOnValidator()
- if (fields.length === 0) {
- touched.value = []
- } else {
- touched.value = touched.value.filter((field) => !fields.includes(field))
- }
+ validator.reset(...fields)
}
const resetAndClearErrors = (...fields: string[]) => {
- form.clearErrors(...fields)
+ clearErrors(...fields)
reset(...fields)
}
const defaults = () => {
defaultData.value = getFormData()
isDirty.value = false
- updateDataOnValidator()
- }
-
- const validate = (
- only?: string | string[] | FormComponentValidateOptions,
- maybeOptions?: FormComponentValidateOptions,
- ) => {
- let fields: string[]
- let options: FormComponentValidateOptions = {}
-
- if (typeof only === 'object' && !Array.isArray(only)) {
- // Called as validate({ only: [...], onSuccess, onError, onFinish })
- const onlyFields = only.only
- fields = onlyFields === undefined ? touched.value : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
- options = only
- } else {
- // Called as validate('field') or validate(['field1', 'field2']) or validate('field', {options})
- fields = only === undefined ? touched.value : Array.isArray(only) ? only : [only]
- options = maybeOptions || {}
- }
-
- // We're not using the data object from this method as it might be empty
- // on GET requests, and we still want to pass a data object to the
- // validator so it knows the current state of the form.
- const [url] = getUrlAndData()
-
- validator.validate({
- url,
- method: method.value,
- data: props.transform(getData()),
- only: fields,
- errorBag: props.errorBag,
- headers: props.headers,
- simpleValidationErrors: props.simpleValidationErrors,
- onBefore: options.onBefore,
- onPrecognitionSuccess: () => {
- validated.value = [...validated.value, ...fields]
- form.clearErrors(...fields)
- options.onSuccess?.()
- },
- onValidationError: (errors) => {
- validated.value = [...validated.value, ...fields]
-
- const validFields = fields.filter((field) => errors[field] === undefined)
-
- if (validFields.length) {
- form.clearErrors(...validFields)
- }
-
- form.setError({ ...form.errors, ...errors })
- options.onError?.(errors)
- },
- onException: options.onException,
- onFinish: () => {
- options.onFinish?.()
- },
- })
- }
-
- const touch = (field: string | string[]) => {
- const fields = Array.isArray(field) ? field : [field]
-
- // Use Set to avoid duplicates
- touched.value = [...new Set([...touched.value, ...fields])]
}
const isTouched = (field?: string): boolean => {
@@ -363,7 +337,7 @@ const Form = defineComponent({
get validating() {
return validating.value
},
- clearErrors: (...fields: string[]) => form.clearErrors(...fields),
+ clearErrors,
resetAndClearErrors,
setError: (fieldOrFields: string | Record, maybeValue?: string) =>
form.setError(typeof fieldOrFields === 'string' ? { [fieldOrFields]: maybeValue } : fieldOrFields),
@@ -375,15 +349,12 @@ const Form = defineComponent({
defaults,
// Precognition
- valid: (field: string) => validated.value.includes(field) && form.errors[field] === undefined,
+ valid: (field: string) => valid.value.includes(field),
invalid: (field: string) => form.errors[field] !== undefined,
- validate,
- touch,
+ validate: (input?: string | NamedInputEvent | ValidationConfig, value?: unknown, config?: ValidationConfig) =>
+ validator!.validate(input, value, config),
+ touch: (...fields: string[]) => validator!.touch(fields),
touched: isTouched,
- cancelValidation: () => {
- validator.cancelAll()
- validating.value = false
- },
}
expose(exposed)
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
index 33fe56981..62972ba06 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
@@ -38,7 +38,7 @@ const handleException = (error: Error) => {
errorCalled = false
finishCalled = false
validate({
- onSuccess: () => {
+ onPrecognitionSuccess: () => {
successCalled = true
},
onFinish: () => {
@@ -59,7 +59,7 @@ const handleException = (error: Error) => {
errorCalled = false
finishCalled = false
validate({
- onError: () => {
+ onValidationError: () => {
errorCalled = true
},
onFinish: () => {
@@ -73,31 +73,5 @@ const handleException = (error: Error) => {
-
-
-
- Exception Test
-
-
-
-
-
- Validating...
- Exception caught: {{ exceptionMessage }}
-
-
-
- Validate with Exception Handler
-
-
- Submit
-
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
index d724d8666..bfb683e60 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCancel.vue
@@ -24,24 +24,5 @@ import { Form } from '@inertiajs/vue3'
Submit
-
-
-
- Manual Cancel Test
-
-
-
-
-
- Validating...
-
- Cancel Validation
- Submit
-
diff --git a/packages/vue3/test-app/package.json b/packages/vue3/test-app/package.json
index 754e96752..d860eafcc 100755
--- a/packages/vue3/test-app/package.json
+++ b/packages/vue3/test-app/package.json
@@ -5,12 +5,12 @@
"type-check": "vue-tsc --noEmit"
},
"devDependencies": {
- "@vitejs/plugin-vue": "^5.0.5",
- "nodemon": "^3.0.0",
- "typescript": "^5.9.2",
- "vite": "^6.3.0",
- "vue": "^3.4.33",
- "vue-tsc": "^2.2.4"
+ "@vitejs/plugin-vue": "^5.2.4",
+ "nodemon": "^3.1.10",
+ "typescript": "^5.9.3",
+ "vite": "^6.4.1",
+ "vue": "^3.5.22",
+ "vue-tsc": "^2.2.12"
},
"dependencies": {
"@inertiajs/core": "workspace:*",
diff --git a/playgrounds/react/package.json b/playgrounds/react/package.json
index 1f7b6d555..11abf6d94 100644
--- a/playgrounds/react/package.json
+++ b/playgrounds/react/package.json
@@ -9,20 +9,20 @@
"devDependencies": {
"@inertiajs/react": "workspace:*",
"@laravel/stream-react": "^0.3.9",
- "@tailwindcss/typography": "^0.5.18",
- "@tailwindcss/vite": "^4.1.13",
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
+ "@tailwindcss/typography": "^0.5.19",
+ "@tailwindcss/vite": "^4.1.16",
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.2",
"@vitejs/plugin-react": "^4.7.0",
"autosize": "^6.0.1",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
- "marked": "^16.3.0",
- "react": "^19.1.1",
- "react-dom": "^19.1.1",
- "tailwindcss": "^4.1.13",
+ "marked": "^16.4.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tailwindcss": "^4.1.16",
"typescript": "^4.9.5",
- "vite": "^6.3.6"
+ "vite": "^6.4.1"
}
}
diff --git a/playgrounds/svelte4/package.json b/playgrounds/svelte4/package.json
index 8def422a7..7e3cbded9 100644
--- a/playgrounds/svelte4/package.json
+++ b/playgrounds/svelte4/package.json
@@ -11,15 +11,15 @@
"devDependencies": {
"@inertiajs/svelte": "workspace:*",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
- "@tailwindcss/vite": "^4.1.13",
+ "@tailwindcss/vite": "^4.1.16",
"@tsconfig/svelte": "^5.0.5",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
"svelte": "^4.2.20",
- "svelte-check": "^4.3.2",
- "tailwindcss": "^4.1.13",
- "typescript": "^5.9.2",
- "vite": "^5.4.20"
+ "svelte-check": "^4.3.3",
+ "tailwindcss": "^4.1.16",
+ "typescript": "^5.9.3",
+ "vite": "^5.4.21"
}
}
diff --git a/playgrounds/svelte5/package.json b/playgrounds/svelte5/package.json
index 8a54a148c..e899d6244 100644
--- a/playgrounds/svelte5/package.json
+++ b/playgrounds/svelte5/package.json
@@ -11,15 +11,15 @@
"devDependencies": {
"@inertiajs/svelte": "workspace:*",
"@sveltejs/vite-plugin-svelte": "^5.1.1",
- "@tailwindcss/vite": "^4.1.13",
+ "@tailwindcss/vite": "^4.1.16",
"@tsconfig/svelte": "^5.0.5",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
- "svelte": "^5.39.5",
- "svelte-check": "^4.3.2",
- "tailwindcss": "^4.1.13",
- "typescript": "^5.9.2",
- "vite": "^6.3.6"
+ "svelte": "^5.43.0",
+ "svelte-check": "^4.3.3",
+ "tailwindcss": "^4.1.16",
+ "typescript": "^5.9.3",
+ "vite": "^6.4.1"
}
}
diff --git a/playgrounds/vue3/package.json b/playgrounds/vue3/package.json
index e089cc932..e1a83d8f7 100644
--- a/playgrounds/vue3/package.json
+++ b/playgrounds/vue3/package.json
@@ -8,19 +8,19 @@
"devDependencies": {
"@inertiajs/vue3": "workspace:*",
"@laravel/stream-vue": "^0.3.9",
- "@tailwindcss/typography": "^0.5.18",
- "@tailwindcss/vite": "^4.1.13",
+ "@tailwindcss/typography": "^0.5.19",
+ "@tailwindcss/vite": "^4.1.16",
"@vitejs/plugin-vue": "^5.2.4",
- "@vue/server-renderer": "^3.5.21",
+ "@vue/server-renderer": "^3.5.22",
"autosize": "^6.0.1",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
- "marked": "^16.3.0",
- "tailwindcss": "^4.1.13",
- "typescript": "^5.9.2",
- "vite": "^6.3.6",
- "vue": "^3.5.21",
+ "marked": "^16.4.1",
+ "tailwindcss": "^4.1.16",
+ "typescript": "^5.9.3",
+ "vite": "^6.4.1",
+ "vue": "^3.5.22",
"vue-tsc": "^2.2.12"
},
"type": "module"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index db148aac0..cd2e1d4c7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,24 +9,24 @@ importers:
.:
dependencies:
'@playwright/test':
- specifier: ^1.55.1
- version: 1.55.1
+ specifier: ^1.56.1
+ version: 1.56.1
prettier:
specifier: ^3.6.2
version: 3.6.2
prettier-plugin-organize-imports:
specifier: ^4.3.0
- version: 4.3.0(prettier@3.6.2)(typescript@5.9.2)(vue-tsc@2.2.12(typescript@5.9.2))
+ version: 4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3))
prettier-plugin-svelte:
specifier: ^3.4.0
- version: 3.4.0(prettier@3.6.2)(svelte@5.39.5)
+ version: 3.4.0(prettier@3.6.2)(svelte@5.43.0)
prettier-plugin-tailwindcss:
specifier: ^0.6.14
- version: 0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.2)(vue-tsc@2.2.12(typescript@5.9.2)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.5))(prettier@3.6.2)
+ version: 0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.0))(prettier@3.6.2)
optionalDependencies:
'@rollup/rollup-linux-x64-gnu':
- specifier: ^4.52.2
- version: 4.52.2
+ specifier: ^4.52.5
+ version: 4.52.5
packages/core:
dependencies:
@@ -34,8 +34,8 @@ importers:
specifier: ^4.17.12
version: 4.17.12
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
lodash-es:
specifier: ^4.17.21
version: 4.17.21
@@ -47,8 +47,8 @@ importers:
specifier: ^2.2.3
version: 2.2.3
'@types/node':
- specifier: ^18.19.127
- version: 18.19.127
+ specifier: ^18.19.130
+ version: 18.19.130
'@types/nprogress':
specifier: ^0.2.3
version: 0.2.3
@@ -56,17 +56,20 @@ importers:
specifier: ^6.14.0
version: 6.14.0
es-check:
- specifier: ^9.3.1
- version: 9.3.1
+ specifier: ^9.4.4
+ version: 9.4.4
esbuild:
- specifier: ^0.25.10
- version: 0.25.10
+ specifier: ^0.25.11
+ version: 0.25.11
esbuild-node-externals:
specifier: ^1.18.0
- version: 1.18.0(esbuild@0.25.10)
+ version: 1.18.0(esbuild@0.25.11)
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
packages/react:
dependencies:
@@ -76,34 +79,37 @@ importers:
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
devDependencies:
'@types/react':
- specifier: ^19.1.13
- version: 19.1.13
+ specifier: ^19.2.2
+ version: 19.2.2
'@types/react-dom':
- specifier: ^19.1.9
- version: 19.1.9(@types/react@19.1.13)
+ specifier: ^19.2.2
+ version: 19.2.2(@types/react@19.2.2)
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
es-check:
- specifier: ^9.3.1
- version: 9.3.1
+ specifier: ^9.4.4
+ version: 9.4.4
esbuild:
- specifier: ^0.25.10
- version: 0.25.10
+ specifier: ^0.25.11
+ version: 0.25.11
esbuild-node-externals:
specifier: ^1.18.0
- version: 1.18.0(esbuild@0.25.10)
+ version: 1.18.0(esbuild@0.25.11)
react:
- specifier: ^19.1.1
- version: 19.1.1
+ specifier: ^19.2.0
+ version: 19.2.0
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
packages/react/test-app:
dependencies:
@@ -114,33 +120,33 @@ importers:
specifier: workspace:*
version: link:..
'@vitejs/plugin-react':
- specifier: ^4.3.1
- version: 4.7.0(vite@6.3.6(@types/node@24.3.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ specifier: ^4.7.0
+ version: 4.7.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
react:
- specifier: ^19.0.0
- version: 19.1.1
+ specifier: ^19.2.0
+ version: 19.2.0
react-dom:
- specifier: ^19.0.0
- version: 19.1.1(react@19.1.1)
+ specifier: ^19.2.0
+ version: 19.2.0(react@19.2.0)
devDependencies:
'@types/node':
- specifier: ^24.1.0
- version: 24.3.2
+ specifier: ^24.9.2
+ version: 24.9.2
'@types/react':
- specifier: ^19.1.9
- version: 19.1.13
+ specifier: ^19.2.2
+ version: 19.2.2
'@types/react-dom':
- specifier: ^19.1.7
- version: 19.1.9(@types/react@19.1.13)
+ specifier: ^19.2.2
+ version: 19.2.2(@types/react@19.2.2)
nodemon:
- specifier: ^3.0.0
+ specifier: ^3.1.10
version: 3.1.10
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^6.3.0
- version: 6.3.6(@types/node@24.3.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^6.4.1
+ version: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
packages/svelte:
dependencies:
@@ -150,28 +156,31 @@ importers:
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^3.3.1
- version: 3.3.1(@sveltejs/kit@2.43.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))
+ version: 3.3.1(@sveltejs/kit@2.48.3(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))
'@sveltejs/kit':
- specifier: ^2.43.2
- version: 2.43.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ specifier: ^2.48.3
+ version: 2.48.3(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
'@sveltejs/package':
specifier: ^2.5.4
- version: 2.5.4(svelte@4.2.20)(typescript@5.9.2)
+ version: 2.5.4(svelte@4.2.20)(typescript@5.9.3)
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.2
- version: 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
es-check:
- specifier: ^9.3.1
- version: 9.3.1
+ specifier: ^9.4.4
+ version: 9.4.4
publint:
specifier: ^0.2.12
version: 0.2.12
@@ -179,17 +188,17 @@ importers:
specifier: ^4.2.20
version: 4.2.20
svelte-check:
- specifier: ^4.3.2
- version: 4.3.2(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.2)
+ specifier: ^4.3.3
+ version: 4.3.3(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.3)
tslib:
specifier: ^2.8.1
version: 2.8.1
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^5.4.20
- version: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^5.4.21
+ version: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
packages/svelte/test-app:
dependencies:
@@ -202,25 +211,25 @@ importers:
devDependencies:
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.2
- version: 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
'@tsconfig/svelte':
- specifier: ^5.0.4
+ specifier: ^5.0.5
version: 5.0.5
nodemon:
- specifier: ^3.0.0
+ specifier: ^3.1.10
version: 3.1.10
svelte:
- specifier: ^4.2.14
+ specifier: ^4.2.20
version: 4.2.20
svelte-check:
- specifier: ^4.1.0
- version: 4.3.1(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.2)
+ specifier: ^4.3.3
+ version: 4.3.3(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.3)
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^5.4.20
- version: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^5.4.21
+ version: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
packages/vue3:
dependencies:
@@ -230,31 +239,34 @@ importers:
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
devDependencies:
'@types/node':
- specifier: ^22.18.6
- version: 22.18.6
+ specifier: ^22.18.13
+ version: 22.18.13
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
es-check:
- specifier: ^9.3.1
- version: 9.3.1
+ specifier: ^9.4.4
+ version: 9.4.4
esbuild:
- specifier: ^0.25.10
- version: 0.25.10
+ specifier: ^0.25.11
+ version: 0.25.11
esbuild-node-externals:
specifier: ^1.18.0
- version: 1.18.0(esbuild@0.25.10)
+ version: 1.18.0(esbuild@0.25.11)
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vue:
- specifier: ^3.5.21
- version: 3.5.21(typescript@5.9.2)
+ specifier: ^3.5.22
+ version: 3.5.22(typescript@5.9.3)
packages/vue3/test-app:
dependencies:
@@ -266,23 +278,23 @@ importers:
version: link:..
devDependencies:
'@vitejs/plugin-vue':
- specifier: ^5.0.5
- version: 5.2.4(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2))
+ specifier: ^5.2.4
+ version: 5.2.4(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))
nodemon:
- specifier: ^3.0.0
+ specifier: ^3.1.10
version: 3.1.10
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^6.3.0
- version: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^6.4.1
+ version: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vue:
- specifier: ^3.4.33
- version: 3.5.21(typescript@5.9.2)
+ specifier: ^3.5.22
+ version: 3.5.22(typescript@5.9.3)
vue-tsc:
- specifier: ^2.2.4
- version: 2.2.12(typescript@5.9.2)
+ specifier: ^2.2.12
+ version: 2.2.12(typescript@5.9.3)
playgrounds/react:
devDependencies:
@@ -291,52 +303,52 @@ importers:
version: link:../../packages/react
'@laravel/stream-react':
specifier: ^0.3.9
- version: 0.3.9(react@19.1.1)
+ version: 0.3.9(react@19.2.0)
'@tailwindcss/typography':
- specifier: ^0.5.18
- version: 0.5.18(tailwindcss@4.1.13)
+ specifier: ^0.5.19
+ version: 0.5.19(tailwindcss@4.1.16)
'@tailwindcss/vite':
- specifier: ^4.1.13
- version: 4.1.13(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@types/react':
- specifier: ^19.1.13
- version: 19.1.13
+ specifier: ^19.2.2
+ version: 19.2.2
'@types/react-dom':
- specifier: ^19.1.9
- version: 19.1.9(@types/react@19.1.13)
+ specifier: ^19.2.2
+ version: 19.2.2(@types/react@19.2.2)
'@vitejs/plugin-react':
specifier: ^4.7.0
- version: 4.7.0(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 4.7.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
autosize:
specifier: ^6.0.1
version: 6.0.1
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 1.3.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
marked:
- specifier: ^16.3.0
- version: 16.3.0
+ specifier: ^16.4.1
+ version: 16.4.1
react:
- specifier: ^19.1.1
- version: 19.1.1
+ specifier: ^19.2.0
+ version: 19.2.0
react-dom:
- specifier: ^19.1.1
- version: 19.1.1(react@19.1.1)
+ specifier: ^19.2.0
+ version: 19.2.0(react@19.2.0)
tailwindcss:
- specifier: ^4.1.13
- version: 4.1.13
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
specifier: ^4.9.5
version: 4.9.5
vite:
- specifier: ^6.3.6
- version: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^6.4.1
+ version: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
playgrounds/svelte4:
devDependencies:
@@ -345,19 +357,19 @@ importers:
version: link:../../packages/svelte
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.2
- version: 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
'@tailwindcss/vite':
- specifier: ^4.1.13
- version: 4.1.13(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
'@tsconfig/svelte':
specifier: ^5.0.5
version: 5.0.5
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 1.3.0(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
@@ -365,17 +377,17 @@ importers:
specifier: ^4.2.20
version: 4.2.20
svelte-check:
- specifier: ^4.3.2
- version: 4.3.2(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.2)
+ specifier: ^4.3.3
+ version: 4.3.3(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.3)
tailwindcss:
- specifier: ^4.1.13
- version: 4.1.13
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^5.4.20
- version: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^5.4.21
+ version: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
playgrounds/svelte5:
devDependencies:
@@ -384,37 +396,37 @@ importers:
version: link:../../packages/svelte
'@sveltejs/vite-plugin-svelte':
specifier: ^5.1.1
- version: 5.1.1(svelte@5.39.5)(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 5.1.1(svelte@5.43.0)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@tailwindcss/vite':
- specifier: ^4.1.13
- version: 4.1.13(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@tsconfig/svelte':
specifier: ^5.0.5
version: 5.0.5
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 1.3.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
svelte:
- specifier: ^5.39.5
- version: 5.39.5
+ specifier: ^5.43.0
+ version: 5.43.0
svelte-check:
- specifier: ^4.3.2
- version: 4.3.2(picomatch@4.0.3)(svelte@5.39.5)(typescript@5.9.2)
+ specifier: ^4.3.3
+ version: 4.3.3(picomatch@4.0.3)(svelte@5.43.0)(typescript@5.9.3)
tailwindcss:
- specifier: ^4.1.13
- version: 4.1.13
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^6.3.6
- version: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^6.4.1
+ version: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
playgrounds/vue3:
devDependencies:
@@ -423,69 +435,69 @@ importers:
version: link:../../packages/vue3
'@laravel/stream-vue':
specifier: ^0.3.9
- version: 0.3.9(vue@3.5.21(typescript@5.9.2))
+ version: 0.3.9(vue@3.5.22(typescript@5.9.3))
'@tailwindcss/typography':
- specifier: ^0.5.18
- version: 0.5.18(tailwindcss@4.1.13)
+ specifier: ^0.5.19
+ version: 0.5.19(tailwindcss@4.1.16)
'@tailwindcss/vite':
- specifier: ^4.1.13
- version: 4.1.13(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@vitejs/plugin-vue':
specifier: ^5.2.4
- version: 5.2.4(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2))
+ version: 5.2.4(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))
'@vue/server-renderer':
- specifier: ^3.5.21
- version: 3.5.21(vue@3.5.21(typescript@5.9.2))
+ specifier: ^3.5.22
+ version: 3.5.22(vue@3.5.22(typescript@5.9.3))
autosize:
specifier: ^6.0.1
version: 6.0.1
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ version: 1.3.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
marked:
- specifier: ^16.3.0
- version: 16.3.0
+ specifier: ^16.4.1
+ version: 16.4.1
tailwindcss:
- specifier: ^4.1.13
- version: 4.1.13
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
- specifier: ^5.9.2
- version: 5.9.2
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^6.3.6
- version: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ specifier: ^6.4.1
+ version: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vue:
- specifier: ^3.5.21
- version: 3.5.21(typescript@5.9.2)
+ specifier: ^3.5.22
+ version: 3.5.22(typescript@5.9.3)
vue-tsc:
specifier: ^2.2.12
- version: 2.2.12(typescript@5.9.2)
+ version: 2.2.12(typescript@5.9.3)
tests/app:
devDependencies:
body-parser:
- specifier: ^1.19.0
+ specifier: ^1.20.3
version: 1.20.3
css-loader:
- specifier: ^5.0.1
+ specifier: ^5.2.7
version: 5.2.7(webpack@5.101.3)
eslint:
- specifier: ^7.17.0
+ specifier: ^7.32.0
version: 7.32.0
express:
- specifier: ^4.21.1
+ specifier: ^4.21.2
version: 4.21.2
multer:
specifier: ^2.0.2
version: 2.0.2
nodemon:
- specifier: ^3.0.0
+ specifier: ^3.1.10
version: 3.1.10
packages:
@@ -501,16 +513,16 @@ packages:
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
- '@babel/compat-data@7.28.4':
- resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
+ '@babel/compat-data@7.28.5':
+ resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==}
engines: {node: '>=6.9.0'}
- '@babel/core@7.28.4':
- resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
+ '@babel/core@7.28.5':
+ resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==}
engines: {node: '>=6.9.0'}
- '@babel/generator@7.28.3':
- resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
+ '@babel/generator@7.28.5':
+ resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.27.2':
@@ -539,8 +551,8 @@ packages:
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.27.1':
- resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-option@7.27.1':
@@ -555,8 +567,8 @@ packages:
resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==}
engines: {node: '>=6.9.0'}
- '@babel/parser@7.28.4':
- resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
+ '@babel/parser@7.28.5':
+ resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -576,20 +588,20 @@ packages:
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
- '@babel/traverse@7.28.4':
- resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
+ '@babel/traverse@7.28.5':
+ resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.28.4':
- resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
+ '@babel/types@7.28.5':
+ resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
engines: {node: '>=6.9.0'}
'@colors/colors@1.6.0':
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
- '@dabh/diagnostics@2.0.3':
- resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
+ '@dabh/diagnostics@2.0.8':
+ resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
@@ -597,8 +609,8 @@ packages:
cpu: [ppc64]
os: [aix]
- '@esbuild/aix-ppc64@0.25.10':
- resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==}
+ '@esbuild/aix-ppc64@0.25.11':
+ resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
@@ -609,8 +621,8 @@ packages:
cpu: [arm64]
os: [android]
- '@esbuild/android-arm64@0.25.10':
- resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==}
+ '@esbuild/android-arm64@0.25.11':
+ resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
@@ -621,8 +633,8 @@ packages:
cpu: [arm]
os: [android]
- '@esbuild/android-arm@0.25.10':
- resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==}
+ '@esbuild/android-arm@0.25.11':
+ resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
@@ -633,8 +645,8 @@ packages:
cpu: [x64]
os: [android]
- '@esbuild/android-x64@0.25.10':
- resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==}
+ '@esbuild/android-x64@0.25.11':
+ resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
@@ -645,8 +657,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-arm64@0.25.10':
- resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==}
+ '@esbuild/darwin-arm64@0.25.11':
+ resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
@@ -657,8 +669,8 @@ packages:
cpu: [x64]
os: [darwin]
- '@esbuild/darwin-x64@0.25.10':
- resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==}
+ '@esbuild/darwin-x64@0.25.11':
+ resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
@@ -669,8 +681,8 @@ packages:
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-arm64@0.25.10':
- resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==}
+ '@esbuild/freebsd-arm64@0.25.11':
+ resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
@@ -681,8 +693,8 @@ packages:
cpu: [x64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.25.10':
- resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==}
+ '@esbuild/freebsd-x64@0.25.11':
+ resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
@@ -693,8 +705,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm64@0.25.10':
- resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==}
+ '@esbuild/linux-arm64@0.25.11':
+ resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
@@ -705,8 +717,8 @@ packages:
cpu: [arm]
os: [linux]
- '@esbuild/linux-arm@0.25.10':
- resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==}
+ '@esbuild/linux-arm@0.25.11':
+ resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
@@ -717,8 +729,8 @@ packages:
cpu: [ia32]
os: [linux]
- '@esbuild/linux-ia32@0.25.10':
- resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==}
+ '@esbuild/linux-ia32@0.25.11':
+ resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
@@ -729,8 +741,8 @@ packages:
cpu: [loong64]
os: [linux]
- '@esbuild/linux-loong64@0.25.10':
- resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==}
+ '@esbuild/linux-loong64@0.25.11':
+ resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
@@ -741,8 +753,8 @@ packages:
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-mips64el@0.25.10':
- resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==}
+ '@esbuild/linux-mips64el@0.25.11':
+ resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
@@ -753,8 +765,8 @@ packages:
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-ppc64@0.25.10':
- resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==}
+ '@esbuild/linux-ppc64@0.25.11':
+ resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
@@ -765,8 +777,8 @@ packages:
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-riscv64@0.25.10':
- resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==}
+ '@esbuild/linux-riscv64@0.25.11':
+ resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
@@ -777,8 +789,8 @@ packages:
cpu: [s390x]
os: [linux]
- '@esbuild/linux-s390x@0.25.10':
- resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==}
+ '@esbuild/linux-s390x@0.25.11':
+ resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
@@ -789,14 +801,14 @@ packages:
cpu: [x64]
os: [linux]
- '@esbuild/linux-x64@0.25.10':
- resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==}
+ '@esbuild/linux-x64@0.25.11':
+ resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
- '@esbuild/netbsd-arm64@0.25.10':
- resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==}
+ '@esbuild/netbsd-arm64@0.25.11':
+ resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
@@ -807,14 +819,14 @@ packages:
cpu: [x64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.25.10':
- resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==}
+ '@esbuild/netbsd-x64@0.25.11':
+ resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
- '@esbuild/openbsd-arm64@0.25.10':
- resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==}
+ '@esbuild/openbsd-arm64@0.25.11':
+ resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
@@ -825,14 +837,14 @@ packages:
cpu: [x64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.25.10':
- resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==}
+ '@esbuild/openbsd-x64@0.25.11':
+ resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/openharmony-arm64@0.25.10':
- resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==}
+ '@esbuild/openharmony-arm64@0.25.11':
+ resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
@@ -843,8 +855,8 @@ packages:
cpu: [x64]
os: [sunos]
- '@esbuild/sunos-x64@0.25.10':
- resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==}
+ '@esbuild/sunos-x64@0.25.11':
+ resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
@@ -855,8 +867,8 @@ packages:
cpu: [arm64]
os: [win32]
- '@esbuild/win32-arm64@0.25.10':
- resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==}
+ '@esbuild/win32-arm64@0.25.11':
+ resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
@@ -867,8 +879,8 @@ packages:
cpu: [ia32]
os: [win32]
- '@esbuild/win32-ia32@0.25.10':
- resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==}
+ '@esbuild/win32-ia32@0.25.11':
+ resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
@@ -879,8 +891,8 @@ packages:
cpu: [x64]
os: [win32]
- '@esbuild/win32-x64@0.25.10':
- resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==}
+ '@esbuild/win32-x64@0.25.11':
+ resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
@@ -898,10 +910,6 @@ packages:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
deprecated: Use @eslint/object-schema instead
- '@isaacs/fs-minipass@4.0.1':
- resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
- engines: {node: '>=18.0.0'}
-
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -943,8 +951,8 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
- '@playwright/test@1.55.1':
- resolution: {integrity: sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==}
+ '@playwright/test@1.56.1':
+ resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==}
engines: {node: '>=18'}
hasBin: true
@@ -954,121 +962,124 @@ packages:
'@rolldown/pluginutils@1.0.0-beta.27':
resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
- '@rollup/rollup-android-arm-eabi@4.52.2':
- resolution: {integrity: sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==}
+ '@rollup/rollup-android-arm-eabi@4.52.5':
+ resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==}
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm64@4.52.2':
- resolution: {integrity: sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==}
+ '@rollup/rollup-android-arm64@4.52.5':
+ resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.52.2':
- resolution: {integrity: sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==}
+ '@rollup/rollup-darwin-arm64@4.52.5':
+ resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==}
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.52.2':
- resolution: {integrity: sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==}
+ '@rollup/rollup-darwin-x64@4.52.5':
+ resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.52.2':
- resolution: {integrity: sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==}
+ '@rollup/rollup-freebsd-arm64@4.52.5':
+ resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.52.2':
- resolution: {integrity: sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==}
+ '@rollup/rollup-freebsd-x64@4.52.5':
+ resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.52.2':
- resolution: {integrity: sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==}
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.5':
+ resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm-musleabihf@4.52.2':
- resolution: {integrity: sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==}
+ '@rollup/rollup-linux-arm-musleabihf@4.52.5':
+ resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm64-gnu@4.52.2':
- resolution: {integrity: sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==}
+ '@rollup/rollup-linux-arm64-gnu@4.52.5':
+ resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-arm64-musl@4.52.2':
- resolution: {integrity: sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==}
+ '@rollup/rollup-linux-arm64-musl@4.52.5':
+ resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-loong64-gnu@4.52.2':
- resolution: {integrity: sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==}
+ '@rollup/rollup-linux-loong64-gnu@4.52.5':
+ resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
cpu: [loong64]
os: [linux]
- '@rollup/rollup-linux-ppc64-gnu@4.52.2':
- resolution: {integrity: sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==}
+ '@rollup/rollup-linux-ppc64-gnu@4.52.5':
+ resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
cpu: [ppc64]
os: [linux]
- '@rollup/rollup-linux-riscv64-gnu@4.52.2':
- resolution: {integrity: sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==}
+ '@rollup/rollup-linux-riscv64-gnu@4.52.5':
+ resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-riscv64-musl@4.52.2':
- resolution: {integrity: sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==}
+ '@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-s390x-gnu@4.52.2':
- resolution: {integrity: sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==}
+ '@rollup/rollup-linux-s390x-gnu@4.52.5':
+ resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
cpu: [s390x]
os: [linux]
- '@rollup/rollup-linux-x64-gnu@4.52.2':
- resolution: {integrity: sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==}
+ '@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-musl@4.52.2':
- resolution: {integrity: sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==}
+ '@rollup/rollup-linux-x64-musl@4.52.5':
+ resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-openharmony-arm64@4.52.2':
- resolution: {integrity: sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==}
+ '@rollup/rollup-openharmony-arm64@4.52.5':
+ resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==}
cpu: [arm64]
os: [openharmony]
- '@rollup/rollup-win32-arm64-msvc@4.52.2':
- resolution: {integrity: sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==}
+ '@rollup/rollup-win32-arm64-msvc@4.52.5':
+ resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==}
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.52.2':
- resolution: {integrity: sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==}
+ '@rollup/rollup-win32-ia32-msvc@4.52.5':
+ resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==}
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-x64-gnu@4.52.2':
- resolution: {integrity: sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==}
+ '@rollup/rollup-win32-x64-gnu@4.52.5':
+ resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==}
cpu: [x64]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.52.2':
- resolution: {integrity: sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==}
+ '@rollup/rollup-win32-x64-msvc@4.52.5':
+ resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==}
cpu: [x64]
os: [win32]
+ '@so-ric/colorspace@1.1.6':
+ resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==}
+
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
- '@sveltejs/acorn-typescript@1.0.5':
- resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==}
+ '@sveltejs/acorn-typescript@1.0.6':
+ resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==}
peerDependencies:
acorn: ^8.9.0
@@ -1077,8 +1088,8 @@ packages:
peerDependencies:
'@sveltejs/kit': ^2.0.0
- '@sveltejs/kit@2.43.2':
- resolution: {integrity: sha512-M27eBoqWmU7dEpotMGBaA1CK6Y/wK1b176mVOkrSJZwt17bk6Z1IjLQv0iuVYm7D8d1Z/I4HzJ3hSqy8auj1Sw==}
+ '@sveltejs/kit@2.48.3':
+ resolution: {integrity: sha512-jf8mx3yctRXE9hvixgcqqK94YI2hDnbxI/12Upkz99XFMvxnJKCMzvz0j7lmbXSyBSNEycWO5xHvi7b73y9qkQ==}
engines: {node: '>=18.13'}
hasBin: true
peerDependencies:
@@ -1127,65 +1138,65 @@ packages:
svelte: ^5.0.0
vite: ^6.0.0
- '@tailwindcss/node@4.1.13':
- resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==}
+ '@tailwindcss/node@4.1.16':
+ resolution: {integrity: sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==}
- '@tailwindcss/oxide-android-arm64@4.1.13':
- resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==}
+ '@tailwindcss/oxide-android-arm64@4.1.16':
+ resolution: {integrity: sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@tailwindcss/oxide-darwin-arm64@4.1.13':
- resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==}
+ '@tailwindcss/oxide-darwin-arm64@4.1.16':
+ resolution: {integrity: sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@tailwindcss/oxide-darwin-x64@4.1.13':
- resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==}
+ '@tailwindcss/oxide-darwin-x64@4.1.16':
+ resolution: {integrity: sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@tailwindcss/oxide-freebsd-x64@4.1.13':
- resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==}
+ '@tailwindcss/oxide-freebsd-x64@4.1.16':
+ resolution: {integrity: sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13':
- resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==}
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16':
+ resolution: {integrity: sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.13':
- resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==}
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.16':
+ resolution: {integrity: sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-musl@4.1.13':
- resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==}
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.16':
+ resolution: {integrity: sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-gnu@4.1.13':
- resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==}
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.16':
+ resolution: {integrity: sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-musl@4.1.13':
- resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==}
+ '@tailwindcss/oxide-linux-x64-musl@4.1.16':
+ resolution: {integrity: sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-wasm32-wasi@4.1.13':
- resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==}
+ '@tailwindcss/oxide-wasm32-wasi@4.1.16':
+ resolution: {integrity: sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
@@ -1196,29 +1207,29 @@ packages:
- '@emnapi/wasi-threads'
- tslib
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.13':
- resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==}
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.16':
+ resolution: {integrity: sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@tailwindcss/oxide-win32-x64-msvc@4.1.13':
- resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==}
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.16':
+ resolution: {integrity: sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@tailwindcss/oxide@4.1.13':
- resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==}
+ '@tailwindcss/oxide@4.1.16':
+ resolution: {integrity: sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==}
engines: {node: '>= 10'}
- '@tailwindcss/typography@0.5.18':
- resolution: {integrity: sha512-dDIgwZOlf+tVkZ7A029VvQ1+ngKATENDjMEx2N35s2yPjfTS05RWSM8ilhEWSa5DMJ6ci2Ha9WNZEd2GQjrdQg==}
+ '@tailwindcss/typography@0.5.19':
+ resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
- '@tailwindcss/vite@4.1.13':
- resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==}
+ '@tailwindcss/vite@4.1.16':
+ resolution: {integrity: sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==}
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
@@ -1262,17 +1273,14 @@ packages:
'@types/lodash@4.17.20':
resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
- '@types/node@18.19.127':
- resolution: {integrity: sha512-gSjxjrnKXML/yo0BO099uPixMqfpJU0TKYjpfLU7TrtA2WWDki412Np/RSTPRil1saKBhvVVKzVx/p/6p94nVA==}
-
- '@types/node@22.18.6':
- resolution: {integrity: sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==}
+ '@types/node@18.19.130':
+ resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
- '@types/node@24.3.2':
- resolution: {integrity: sha512-6L8PkB+m1SSb2kaGGFk3iXENxl8lrs7cyVl7AXH6pgdMfulDfM6yUrVdjtxdnGrLrGzzuav8fFnZMY+rcscqcA==}
+ '@types/node@22.18.13':
+ resolution: {integrity: sha512-Bo45YKIjnmFtv6I1TuC8AaHBbqXtIo+Om5fE4QiU1Tj8QR/qt+8O3BAtOimG5IFmwaWiPmB3Mv3jtYzBA4Us2A==}
- '@types/node@24.5.2':
- resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==}
+ '@types/node@24.9.2':
+ resolution: {integrity: sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==}
'@types/nprogress@0.2.3':
resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
@@ -1280,13 +1288,13 @@ packages:
'@types/qs@6.14.0':
resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==}
- '@types/react-dom@19.1.9':
- resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
+ '@types/react-dom@19.2.2':
+ resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==}
peerDependencies:
- '@types/react': ^19.0.0
+ '@types/react': ^19.2.0
- '@types/react@19.1.13':
- resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==}
+ '@types/react@19.2.2':
+ resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==}
'@types/triple-beam@1.3.5':
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
@@ -1313,17 +1321,17 @@ packages:
'@volar/typescript@2.4.15':
resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
- '@vue/compiler-core@3.5.21':
- resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==}
+ '@vue/compiler-core@3.5.22':
+ resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==}
- '@vue/compiler-dom@3.5.21':
- resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==}
+ '@vue/compiler-dom@3.5.22':
+ resolution: {integrity: sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==}
- '@vue/compiler-sfc@3.5.21':
- resolution: {integrity: sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==}
+ '@vue/compiler-sfc@3.5.22':
+ resolution: {integrity: sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==}
- '@vue/compiler-ssr@3.5.21':
- resolution: {integrity: sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==}
+ '@vue/compiler-ssr@3.5.22':
+ resolution: {integrity: sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==}
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
@@ -1336,22 +1344,22 @@ packages:
typescript:
optional: true
- '@vue/reactivity@3.5.21':
- resolution: {integrity: sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==}
+ '@vue/reactivity@3.5.22':
+ resolution: {integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==}
- '@vue/runtime-core@3.5.21':
- resolution: {integrity: sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==}
+ '@vue/runtime-core@3.5.22':
+ resolution: {integrity: sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==}
- '@vue/runtime-dom@3.5.21':
- resolution: {integrity: sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==}
+ '@vue/runtime-dom@3.5.22':
+ resolution: {integrity: sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==}
- '@vue/server-renderer@3.5.21':
- resolution: {integrity: sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==}
+ '@vue/server-renderer@3.5.22':
+ resolution: {integrity: sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==}
peerDependencies:
- vue: 3.5.21
+ vue: 3.5.22
- '@vue/shared@3.5.21':
- resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==}
+ '@vue/shared@3.5.22':
+ resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==}
'@webassemblyjs/ast@1.14.1':
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
@@ -1506,8 +1514,8 @@ packages:
autosize@6.0.1:
resolution: {integrity: sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==}
- axios@1.12.2:
- resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==}
+ axios@1.13.1:
+ resolution: {integrity: sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==}
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
@@ -1516,8 +1524,8 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
- baseline-browser-mapping@2.8.6:
- resolution: {integrity: sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==}
+ baseline-browser-mapping@2.8.21:
+ resolution: {integrity: sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q==}
hasBin: true
big.js@5.2.2:
@@ -1541,8 +1549,8 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
- browserslist@4.26.2:
- resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==}
+ browserslist@4.27.0:
+ resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -1569,8 +1577,8 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
- caniuse-lite@1.0.30001743:
- resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==}
+ caniuse-lite@1.0.30001751:
+ resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@@ -1588,10 +1596,6 @@ packages:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
- chownr@3.0.0:
- resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
- engines: {node: '>=18'}
-
chrome-trace-event@1.0.4:
resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
engines: {node: '>=6.0'}
@@ -1610,27 +1614,34 @@ packages:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
+ color-convert@3.1.2:
+ resolution: {integrity: sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==}
+ engines: {node: '>=14.6'}
+
color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
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-name@2.0.2:
+ resolution: {integrity: sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==}
+ engines: {node: '>=12.20'}
- color@3.2.1:
- resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
+ color-string@2.1.2:
+ resolution: {integrity: sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==}
+ engines: {node: '>=18'}
- colorspace@1.1.4:
- resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
+ color@5.0.2:
+ resolution: {integrity: sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==}
+ engines: {node: '>=18'}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
- commander@14.0.0:
- resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==}
+ commander@14.0.1:
+ resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==}
engines: {node: '>=20'}
commander@2.20.3:
@@ -1698,15 +1709,6 @@ packages:
supports-color:
optional: true
- debug@4.4.1:
- resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
@@ -1738,12 +1740,12 @@ packages:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
- detect-libc@2.1.0:
- resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==}
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
- devalue@5.3.2:
- resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==}
+ devalue@5.4.2:
+ resolution: {integrity: sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==}
doctrine@3.0.0:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
@@ -1756,8 +1758,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
- electron-to-chromium@1.5.223:
- resolution: {integrity: sha512-qKm55ic6nbEmagFlTFczML33rF90aU+WtrJ9MdTCThrcvDNdUHN4p6QfVN78U06ZmguqXIyMPyYhw2TrbDUwPQ==}
+ electron-to-chromium@1.5.243:
+ resolution: {integrity: sha512-ZCphxFW3Q1TVhcgS9blfut1PX8lusVi2SvXQgmEEnK4TCmE1JhH2JkjJN+DNt0pJJwfBri5AROBnz2b/C+YU9g==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -1789,8 +1791,8 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
- es-check@9.3.1:
- resolution: {integrity: sha512-9cDv061bddtgqtqc6xbxA1mfwNYQsNGyaiElpQhcNycTaW7Pnpk183jy+3eOjhMSbZCy/RqCfu2lZZufrlo2mA==}
+ es-check@9.4.4:
+ resolution: {integrity: sha512-Ppp6r1diw1jy0t5uQX47HN3JqvosoAymZshdimrwpxCY1GQfZvqTqb9tHiiDbkm+aqGLdVDCZoL9okue7tdXZw==}
engines: {node: '>= 4'}
hasBin: true
@@ -1824,8 +1826,8 @@ packages:
engines: {node: '>=12'}
hasBin: true
- esbuild@0.25.10:
- resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==}
+ esbuild@0.25.11:
+ resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==}
engines: {node: '>=18'}
hasBin: true
@@ -1882,8 +1884,8 @@ packages:
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
- esrap@2.1.0:
- resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==}
+ esrap@2.1.1:
+ resolution: {integrity: sha512-ebTT9B6lOtZGMgJ3o5r12wBacHctG7oEWazIda8UlPfA3HD/Wrv8FdXoVo73vzdpwCxNyXjPauyN2bbJzMkB9A==}
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
@@ -1919,6 +1921,9 @@ packages:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
+ fast-brake@0.1.6:
+ resolution: {integrity: sha512-V3j0HTIs70OOxRpbqT0bWVdrmP86s6N8TBPw/WyKJqdJ2Fwh3CuMmlO83uIIDpqik6m1Io9fT6qwNn69EqvXYA==}
+
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -2126,9 +2131,6 @@ packages:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
- is-arrayish@0.3.4:
- resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==}
-
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@@ -2163,8 +2165,8 @@ packages:
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
engines: {node: '>= 10.13.0'}
- jiti@2.6.0:
- resolution: {integrity: sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==}
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
js-tokens@4.0.0:
@@ -2209,6 +2211,9 @@ packages:
kuler@2.0.0:
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
+ laravel-precognition@0.7.3:
+ resolution: {integrity: sha512-Z97i35Q0wmsRC9WFUY2EmFS2W1F6FF/HakwZg6PqSaR9lHBgbdIs1YZpDlkChVkgcmmvbN19ujKss3ZqbjPM1g==}
+
laravel-vite-plugin@1.3.0:
resolution: {integrity: sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -2220,76 +2225,82 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
- lightningcss-darwin-arm64@1.30.1:
- resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+ lightningcss-android-arm64@1.30.2:
+ resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.30.2:
+ resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
- lightningcss-darwin-x64@1.30.1:
- resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+ lightningcss-darwin-x64@1.30.2:
+ resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
- lightningcss-freebsd-x64@1.30.1:
- resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+ lightningcss-freebsd-x64@1.30.2:
+ resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
- lightningcss-linux-arm-gnueabihf@1.30.1:
- resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+ lightningcss-linux-arm-gnueabihf@1.30.2:
+ resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
- lightningcss-linux-arm64-gnu@1.30.1:
- resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+ lightningcss-linux-arm64-gnu@1.30.2:
+ resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- lightningcss-linux-arm64-musl@1.30.1:
- resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+ lightningcss-linux-arm64-musl@1.30.2:
+ resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- lightningcss-linux-x64-gnu@1.30.1:
- resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+ lightningcss-linux-x64-gnu@1.30.2:
+ resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- lightningcss-linux-x64-musl@1.30.1:
- resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+ lightningcss-linux-x64-musl@1.30.2:
+ resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- lightningcss-win32-arm64-msvc@1.30.1:
- resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+ lightningcss-win32-arm64-msvc@1.30.2:
+ resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
- lightningcss-win32-x64-msvc@1.30.1:
- resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+ lightningcss-win32-x64-msvc@1.30.2:
+ resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
- lightningcss@1.30.1:
- resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+ lightningcss@1.30.2:
+ resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
engines: {node: '>= 12.0.0'}
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
- loader-runner@4.3.0:
- resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
+ loader-runner@4.3.1:
+ resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==}
engines: {node: '>=6.11.5'}
loader-utils@2.0.4:
@@ -2322,11 +2333,11 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
- magic-string@0.30.19:
- resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
- marked@16.3.0:
- resolution: {integrity: sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==}
+ marked@16.4.1:
+ resolution: {integrity: sha512-ntROs7RaN3EvWfy3EZi14H4YxmT6A5YvywfhO+0pm+cH/dnSQRmdAmoFIc3B9aiwTehyk7pESH4ofyBY+V5hZg==}
engines: {node: '>= 20'}
hasBin: true
@@ -2386,14 +2397,6 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
- minipass@7.1.2:
- resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
- engines: {node: '>=16 || 14 >=14.17'}
-
- minizlib@3.1.0:
- resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
- engines: {node: '>= 18'}
-
mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
@@ -2439,8 +2442,8 @@ packages:
neo-async@2.6.2:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
- node-releases@2.0.21:
- resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==}
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
nodemon@3.1.10:
resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==}
@@ -2534,13 +2537,13 @@ packages:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
- playwright-core@1.55.1:
- resolution: {integrity: sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==}
+ playwright-core@1.56.1:
+ resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==}
engines: {node: '>=18'}
hasBin: true
- playwright@1.55.1:
- resolution: {integrity: sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==}
+ playwright@1.56.1:
+ resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==}
engines: {node: '>=18'}
hasBin: true
@@ -2714,17 +2717,17 @@ packages:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
- react-dom@19.1.1:
- resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
+ react-dom@19.2.0:
+ resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==}
peerDependencies:
- react: ^19.1.1
+ react: ^19.2.0
react-refresh@0.17.0:
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
engines: {node: '>=0.10.0'}
- react@19.1.1:
- resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
+ react@19.2.0:
+ resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
engines: {node: '>=0.10.0'}
readable-stream@3.6.2:
@@ -2760,8 +2763,8 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
- rollup@4.52.2:
- resolution: {integrity: sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==}
+ rollup@4.52.5:
+ resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -2782,15 +2785,15 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
- scheduler@0.26.0:
- resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
schema-utils@3.3.0:
resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
engines: {node: '>= 10.13.0'}
- schema-utils@4.3.2:
- resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==}
+ schema-utils@4.3.3:
+ resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==}
engines: {node: '>= 10.13.0'}
scule@1.3.0:
@@ -2800,8 +2803,8 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
- semver@7.7.2:
- resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
hasBin: true
@@ -2816,8 +2819,8 @@ packages:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
- set-cookie-parser@2.7.1:
- resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+ set-cookie-parser@2.7.2:
+ resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
@@ -2846,9 +2849,6 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
- simple-swizzle@0.2.4:
- resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
-
simple-update-notifier@2.0.0:
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
engines: {node: '>=10'}
@@ -2917,16 +2917,8 @@ packages:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
- svelte-check@4.3.1:
- resolution: {integrity: sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg==}
- engines: {node: '>= 18.0.0'}
- hasBin: true
- peerDependencies:
- svelte: ^4.0.0 || ^5.0.0-next.0
- typescript: '>=5.0.0'
-
- svelte-check@4.3.2:
- resolution: {integrity: sha512-71udP5w2kaSTcX8iV0hn3o2FWlabQHhJTJLIQrCqMsrcOeDUO2VhCQKKCA8AMVHSPwdxLEWkUWh9OKxns5PD9w==}
+ svelte-check@4.3.3:
+ resolution: {integrity: sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==}
engines: {node: '>= 18.0.0'}
hasBin: true
peerDependencies:
@@ -2939,8 +2931,8 @@ packages:
peerDependencies:
svelte: ^3.19.0 || ^4.0.0
- svelte2tsx@0.7.44:
- resolution: {integrity: sha512-opuH+bCboss0/ncxnfAO+qt0IAprxc8OqwuC7otafWeO5CHjJ6UAAwvQmu/+xjpCSarX8pQKydXQuoJmbCDcTg==}
+ svelte2tsx@0.7.45:
+ resolution: {integrity: sha512-cSci+mYGygYBHIZLHlm/jYlEc1acjAHqaQaDFHdEBpUueM9kSTnPpvPtSl5VkJOU1qSJ7h1K+6F/LIUYiqC8VA==}
peerDependencies:
svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0
typescript: ^4.9.4 || ^5.0.0
@@ -2949,25 +2941,21 @@ packages:
resolution: {integrity: sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==}
engines: {node: '>=16'}
- svelte@5.39.5:
- resolution: {integrity: sha512-YrTmQAgJNB5He5t14g+BH76xTjskcx0Dg3p6qKqfPcgha+9Rzdgkoazdk18ahzNfkFYgykZIjfnBvlPcp3NpYg==}
+ svelte@5.43.0:
+ resolution: {integrity: sha512-1sRxVbgJAB+UGzwkc3GUoiBSzEOf0jqzccMaVoI2+pI+kASUe9qubslxace8+Mzhqw19k4syTA5niCIJwfXpOA==}
engines: {node: '>=18'}
table@6.9.0:
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
engines: {node: '>=10.0.0'}
- tailwindcss@4.1.13:
- resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==}
+ tailwindcss@4.1.16:
+ resolution: {integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==}
- tapable@2.2.3:
- resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==}
+ tapable@2.3.0:
+ resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'}
- tar@7.5.1:
- resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==}
- engines: {node: '>=18'}
-
terser-webpack-plugin@5.3.14:
resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==}
engines: {node: '>= 10.13.0'}
@@ -3042,8 +3030,8 @@ packages:
engines: {node: '>=4.2.0'}
hasBin: true
- typescript@5.9.2:
- resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
@@ -3056,18 +3044,15 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
- undici-types@7.10.0:
- resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
-
- undici-types@7.12.0:
- resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==}
+ undici-types@7.16.0:
+ resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
- update-browserslist-db@1.1.3:
- resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ update-browserslist-db@1.1.4:
+ resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
@@ -3092,8 +3077,8 @@ packages:
vite-plugin-full-reload@1.2.0:
resolution: {integrity: sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==}
- vite@5.4.20:
- resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==}
+ vite@5.4.21:
+ resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -3123,8 +3108,8 @@ packages:
terser:
optional: true
- vite@6.3.6:
- resolution: {integrity: sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==}
+ vite@6.4.1:
+ resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
@@ -3188,8 +3173,8 @@ packages:
peerDependencies:
typescript: '>=5.0.0'
- vue@3.5.21:
- resolution: {integrity: sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==}
+ vue@3.5.22:
+ resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@@ -3241,10 +3226,6 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
- yallist@5.0.0:
- resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
- engines: {node: '>=18'}
-
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
@@ -3265,45 +3246,45 @@ snapshots:
'@babel/code-frame@7.27.1':
dependencies:
- '@babel/helper-validator-identifier': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
js-tokens: 4.0.0
picocolors: 1.1.1
- '@babel/compat-data@7.28.4': {}
+ '@babel/compat-data@7.28.5': {}
- '@babel/core@7.28.4':
+ '@babel/core@7.28.5':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/generator': 7.28.3
+ '@babel/generator': 7.28.5
'@babel/helper-compilation-targets': 7.27.2
- '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5)
'@babel/helpers': 7.28.4
- '@babel/parser': 7.28.4
+ '@babel/parser': 7.28.5
'@babel/template': 7.27.2
- '@babel/traverse': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
- debug: 4.4.3
+ debug: 4.4.3(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
transitivePeerDependencies:
- supports-color
- '@babel/generator@7.28.3':
+ '@babel/generator@7.28.5':
dependencies:
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
jsesc: 3.1.0
'@babel/helper-compilation-targets@7.27.2':
dependencies:
- '@babel/compat-data': 7.28.4
+ '@babel/compat-data': 7.28.5
'@babel/helper-validator-option': 7.27.1
- browserslist: 4.26.2
+ browserslist: 4.27.0
lru-cache: 5.1.1
semver: 6.3.1
@@ -3311,17 +3292,17 @@ snapshots:
'@babel/helper-module-imports@7.27.1':
dependencies:
- '@babel/traverse': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)':
dependencies:
- '@babel/core': 7.28.4
+ '@babel/core': 7.28.5
'@babel/helper-module-imports': 7.27.1
- '@babel/helper-validator-identifier': 7.27.1
- '@babel/traverse': 7.28.4
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.28.5
transitivePeerDependencies:
- supports-color
@@ -3329,218 +3310,218 @@ snapshots:
'@babel/helper-string-parser@7.27.1': {}
- '@babel/helper-validator-identifier@7.27.1': {}
+ '@babel/helper-validator-identifier@7.28.5': {}
'@babel/helper-validator-option@7.27.1': {}
'@babel/helpers@7.28.4':
dependencies:
'@babel/template': 7.27.2
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
'@babel/highlight@7.25.9':
dependencies:
- '@babel/helper-validator-identifier': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
chalk: 2.4.2
js-tokens: 4.0.0
picocolors: 1.1.1
- '@babel/parser@7.28.4':
+ '@babel/parser@7.28.5':
dependencies:
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
- '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)':
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)':
dependencies:
- '@babel/core': 7.28.4
+ '@babel/core': 7.28.5
'@babel/helper-plugin-utils': 7.27.1
- '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)':
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)':
dependencies:
- '@babel/core': 7.28.4
+ '@babel/core': 7.28.5
'@babel/helper-plugin-utils': 7.27.1
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
- '@babel/traverse@7.28.4':
+ '@babel/traverse@7.28.5':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/generator': 7.28.3
+ '@babel/generator': 7.28.5
'@babel/helper-globals': 7.28.0
- '@babel/parser': 7.28.4
+ '@babel/parser': 7.28.5
'@babel/template': 7.27.2
- '@babel/types': 7.28.4
- debug: 4.4.3
+ '@babel/types': 7.28.5
+ debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
- '@babel/types@7.28.4':
+ '@babel/types@7.28.5':
dependencies:
'@babel/helper-string-parser': 7.27.1
- '@babel/helper-validator-identifier': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
'@colors/colors@1.6.0': {}
- '@dabh/diagnostics@2.0.3':
+ '@dabh/diagnostics@2.0.8':
dependencies:
- colorspace: 1.1.4
+ '@so-ric/colorspace': 1.1.6
enabled: 2.0.0
kuler: 2.0.0
'@esbuild/aix-ppc64@0.21.5':
optional: true
- '@esbuild/aix-ppc64@0.25.10':
+ '@esbuild/aix-ppc64@0.25.11':
optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
- '@esbuild/android-arm64@0.25.10':
+ '@esbuild/android-arm64@0.25.11':
optional: true
'@esbuild/android-arm@0.21.5':
optional: true
- '@esbuild/android-arm@0.25.10':
+ '@esbuild/android-arm@0.25.11':
optional: true
'@esbuild/android-x64@0.21.5':
optional: true
- '@esbuild/android-x64@0.25.10':
+ '@esbuild/android-x64@0.25.11':
optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
- '@esbuild/darwin-arm64@0.25.10':
+ '@esbuild/darwin-arm64@0.25.11':
optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
- '@esbuild/darwin-x64@0.25.10':
+ '@esbuild/darwin-x64@0.25.11':
optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
- '@esbuild/freebsd-arm64@0.25.10':
+ '@esbuild/freebsd-arm64@0.25.11':
optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
- '@esbuild/freebsd-x64@0.25.10':
+ '@esbuild/freebsd-x64@0.25.11':
optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
- '@esbuild/linux-arm64@0.25.10':
+ '@esbuild/linux-arm64@0.25.11':
optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
- '@esbuild/linux-arm@0.25.10':
+ '@esbuild/linux-arm@0.25.11':
optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
- '@esbuild/linux-ia32@0.25.10':
+ '@esbuild/linux-ia32@0.25.11':
optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
- '@esbuild/linux-loong64@0.25.10':
+ '@esbuild/linux-loong64@0.25.11':
optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
- '@esbuild/linux-mips64el@0.25.10':
+ '@esbuild/linux-mips64el@0.25.11':
optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
- '@esbuild/linux-ppc64@0.25.10':
+ '@esbuild/linux-ppc64@0.25.11':
optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
- '@esbuild/linux-riscv64@0.25.10':
+ '@esbuild/linux-riscv64@0.25.11':
optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
- '@esbuild/linux-s390x@0.25.10':
+ '@esbuild/linux-s390x@0.25.11':
optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
- '@esbuild/linux-x64@0.25.10':
+ '@esbuild/linux-x64@0.25.11':
optional: true
- '@esbuild/netbsd-arm64@0.25.10':
+ '@esbuild/netbsd-arm64@0.25.11':
optional: true
'@esbuild/netbsd-x64@0.21.5':
optional: true
- '@esbuild/netbsd-x64@0.25.10':
+ '@esbuild/netbsd-x64@0.25.11':
optional: true
- '@esbuild/openbsd-arm64@0.25.10':
+ '@esbuild/openbsd-arm64@0.25.11':
optional: true
'@esbuild/openbsd-x64@0.21.5':
optional: true
- '@esbuild/openbsd-x64@0.25.10':
+ '@esbuild/openbsd-x64@0.25.11':
optional: true
- '@esbuild/openharmony-arm64@0.25.10':
+ '@esbuild/openharmony-arm64@0.25.11':
optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
- '@esbuild/sunos-x64@0.25.10':
+ '@esbuild/sunos-x64@0.25.11':
optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
- '@esbuild/win32-arm64@0.25.10':
+ '@esbuild/win32-arm64@0.25.11':
optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
- '@esbuild/win32-ia32@0.25.10':
+ '@esbuild/win32-ia32@0.25.11':
optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
- '@esbuild/win32-x64@0.25.10':
+ '@esbuild/win32-x64@0.25.11':
optional: true
'@eslint/eslintrc@0.4.3':
dependencies:
ajv: 6.12.6
- debug: 4.4.1(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@5.5.0)
espree: 7.3.1
globals: 13.24.0
ignore: 4.0.6
@@ -3554,17 +3535,13 @@ snapshots:
'@humanwhocodes/config-array@0.5.0':
dependencies:
'@humanwhocodes/object-schema': 1.2.1
- debug: 4.4.1(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
'@humanwhocodes/object-schema@1.2.1': {}
- '@isaacs/fs-minipass@4.0.1':
- dependencies:
- minipass: 7.1.2
-
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -3589,14 +3566,14 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
- '@laravel/stream-react@0.3.9(react@19.1.1)':
+ '@laravel/stream-react@0.3.9(react@19.2.0)':
dependencies:
- react: 19.1.1
+ react: 19.2.0
- '@laravel/stream-vue@0.3.9(vue@3.5.21(typescript@5.9.2))':
+ '@laravel/stream-vue@0.3.9(vue@3.5.22(typescript@5.9.3))':
dependencies:
nanoid: 5.1.6
- vue: 3.5.21(typescript@5.9.2)
+ vue: 3.5.22(typescript@5.9.3)
'@nodelib/fs.scandir@2.1.5':
dependencies:
@@ -3610,271 +3587,273 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
- '@playwright/test@1.55.1':
+ '@playwright/test@1.56.1':
dependencies:
- playwright: 1.55.1
+ playwright: 1.56.1
'@polka/url@1.0.0-next.29': {}
'@rolldown/pluginutils@1.0.0-beta.27': {}
- '@rollup/rollup-android-arm-eabi@4.52.2':
+ '@rollup/rollup-android-arm-eabi@4.52.5':
optional: true
- '@rollup/rollup-android-arm64@4.52.2':
+ '@rollup/rollup-android-arm64@4.52.5':
optional: true
- '@rollup/rollup-darwin-arm64@4.52.2':
+ '@rollup/rollup-darwin-arm64@4.52.5':
optional: true
- '@rollup/rollup-darwin-x64@4.52.2':
+ '@rollup/rollup-darwin-x64@4.52.5':
optional: true
- '@rollup/rollup-freebsd-arm64@4.52.2':
+ '@rollup/rollup-freebsd-arm64@4.52.5':
optional: true
- '@rollup/rollup-freebsd-x64@4.52.2':
+ '@rollup/rollup-freebsd-x64@4.52.5':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.52.2':
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.5':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.52.2':
+ '@rollup/rollup-linux-arm-musleabihf@4.52.5':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.52.2':
+ '@rollup/rollup-linux-arm64-gnu@4.52.5':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.52.2':
+ '@rollup/rollup-linux-arm64-musl@4.52.5':
optional: true
- '@rollup/rollup-linux-loong64-gnu@4.52.2':
+ '@rollup/rollup-linux-loong64-gnu@4.52.5':
optional: true
- '@rollup/rollup-linux-ppc64-gnu@4.52.2':
+ '@rollup/rollup-linux-ppc64-gnu@4.52.5':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.52.2':
+ '@rollup/rollup-linux-riscv64-gnu@4.52.5':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.52.2':
+ '@rollup/rollup-linux-riscv64-musl@4.52.5':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.52.2':
+ '@rollup/rollup-linux-s390x-gnu@4.52.5':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.52.2':
+ '@rollup/rollup-linux-x64-gnu@4.52.5':
optional: true
- '@rollup/rollup-linux-x64-musl@4.52.2':
+ '@rollup/rollup-linux-x64-musl@4.52.5':
optional: true
- '@rollup/rollup-openharmony-arm64@4.52.2':
+ '@rollup/rollup-openharmony-arm64@4.52.5':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.52.2':
+ '@rollup/rollup-win32-arm64-msvc@4.52.5':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.52.2':
+ '@rollup/rollup-win32-ia32-msvc@4.52.5':
optional: true
- '@rollup/rollup-win32-x64-gnu@4.52.2':
+ '@rollup/rollup-win32-x64-gnu@4.52.5':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.52.2':
+ '@rollup/rollup-win32-x64-msvc@4.52.5':
optional: true
+ '@so-ric/colorspace@1.1.6':
+ dependencies:
+ color: 5.0.2
+ text-hex: 1.0.0
+
'@standard-schema/spec@1.0.0': {}
- '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)':
+ '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)':
dependencies:
acorn: 8.15.0
- '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.43.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))':
+ '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.48.3(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))':
dependencies:
- '@sveltejs/kit': 2.43.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ '@sveltejs/kit': 2.48.3(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
import-meta-resolve: 4.2.0
- '@sveltejs/kit@2.43.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@sveltejs/kit@2.48.3(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
'@standard-schema/spec': 1.0.0
- '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
- '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0)
+ '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
'@types/cookie': 0.6.0
acorn: 8.15.0
cookie: 0.6.0
- devalue: 5.3.2
+ devalue: 5.4.2
esm-env: 1.2.2
kleur: 4.1.5
- magic-string: 0.30.19
+ magic-string: 0.30.21
mrmime: 2.0.1
sade: 1.8.1
- set-cookie-parser: 2.7.1
+ set-cookie-parser: 2.7.2
sirv: 3.0.2
svelte: 4.2.20
- vite: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
- '@sveltejs/package@2.5.4(svelte@4.2.20)(typescript@5.9.2)':
+ '@sveltejs/package@2.5.4(svelte@4.2.20)(typescript@5.9.3)':
dependencies:
chokidar: 4.0.3
kleur: 4.1.5
sade: 1.8.1
- semver: 7.7.2
+ semver: 7.7.3
svelte: 4.2.20
- svelte2tsx: 0.7.44(svelte@4.2.20)(typescript@5.9.2)
+ svelte2tsx: 0.7.45(svelte@4.2.20)(typescript@5.9.3)
transitivePeerDependencies:
- typescript
- '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
- debug: 4.4.3
+ '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
+ debug: 4.4.3(supports-color@5.5.0)
svelte: 4.2.20
- vite: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.39.5)(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@5.39.5)(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.0)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.0)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.39.5)(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
- debug: 4.4.3
- svelte: 5.39.5
- vite: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.43.0)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ debug: 4.4.3(supports-color@5.5.0)
+ svelte: 5.43.0
+ vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
- debug: 4.4.3
+ '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
+ debug: 4.4.3(supports-color@5.5.0)
deepmerge: 4.3.1
kleur: 4.1.5
- magic-string: 0.30.19
+ magic-string: 0.30.21
svelte: 4.2.20
svelte-hmr: 0.16.0(svelte@4.2.20)
- vite: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
- vitefu: 0.2.5(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))
+ vite: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
+ vitefu: 0.2.5(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.39.5)(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.0)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.39.5)(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)))(svelte@5.39.5)(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
- debug: 4.4.3
+ '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.0)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.0)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ debug: 4.4.3(supports-color@5.5.0)
deepmerge: 4.3.1
kleur: 4.1.5
- magic-string: 0.30.19
- svelte: 5.39.5
- vite: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
- vitefu: 1.1.1(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))
+ magic-string: 0.30.21
+ svelte: 5.43.0
+ vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vitefu: 1.1.1(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
transitivePeerDependencies:
- supports-color
- '@tailwindcss/node@4.1.13':
+ '@tailwindcss/node@4.1.16':
dependencies:
'@jridgewell/remapping': 2.3.5
enhanced-resolve: 5.18.3
- jiti: 2.6.0
- lightningcss: 1.30.1
- magic-string: 0.30.19
+ jiti: 2.6.1
+ lightningcss: 1.30.2
+ magic-string: 0.30.21
source-map-js: 1.2.1
- tailwindcss: 4.1.13
+ tailwindcss: 4.1.16
- '@tailwindcss/oxide-android-arm64@4.1.13':
+ '@tailwindcss/oxide-android-arm64@4.1.16':
optional: true
- '@tailwindcss/oxide-darwin-arm64@4.1.13':
+ '@tailwindcss/oxide-darwin-arm64@4.1.16':
optional: true
- '@tailwindcss/oxide-darwin-x64@4.1.13':
+ '@tailwindcss/oxide-darwin-x64@4.1.16':
optional: true
- '@tailwindcss/oxide-freebsd-x64@4.1.13':
+ '@tailwindcss/oxide-freebsd-x64@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13':
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.13':
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-arm64-musl@4.1.13':
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-x64-gnu@4.1.13':
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-x64-musl@4.1.13':
+ '@tailwindcss/oxide-linux-x64-musl@4.1.16':
optional: true
- '@tailwindcss/oxide-wasm32-wasi@4.1.13':
+ '@tailwindcss/oxide-wasm32-wasi@4.1.16':
optional: true
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.13':
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.16':
optional: true
- '@tailwindcss/oxide-win32-x64-msvc@4.1.13':
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.16':
optional: true
- '@tailwindcss/oxide@4.1.13':
- dependencies:
- detect-libc: 2.1.0
- tar: 7.5.1
+ '@tailwindcss/oxide@4.1.16':
optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.1.13
- '@tailwindcss/oxide-darwin-arm64': 4.1.13
- '@tailwindcss/oxide-darwin-x64': 4.1.13
- '@tailwindcss/oxide-freebsd-x64': 4.1.13
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13
- '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13
- '@tailwindcss/oxide-linux-arm64-musl': 4.1.13
- '@tailwindcss/oxide-linux-x64-gnu': 4.1.13
- '@tailwindcss/oxide-linux-x64-musl': 4.1.13
- '@tailwindcss/oxide-wasm32-wasi': 4.1.13
- '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13
- '@tailwindcss/oxide-win32-x64-msvc': 4.1.13
-
- '@tailwindcss/typography@0.5.18(tailwindcss@4.1.13)':
+ '@tailwindcss/oxide-android-arm64': 4.1.16
+ '@tailwindcss/oxide-darwin-arm64': 4.1.16
+ '@tailwindcss/oxide-darwin-x64': 4.1.16
+ '@tailwindcss/oxide-freebsd-x64': 4.1.16
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.16
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.16
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.16
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.16
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.16
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.16
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.16
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.16
+
+ '@tailwindcss/typography@0.5.19(tailwindcss@4.1.16)':
dependencies:
postcss-selector-parser: 6.0.10
- tailwindcss: 4.1.13
+ tailwindcss: 4.1.16
- '@tailwindcss/vite@4.1.13(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@tailwindcss/vite@4.1.16(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@tailwindcss/node': 4.1.13
- '@tailwindcss/oxide': 4.1.13
- tailwindcss: 4.1.13
- vite: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ '@tailwindcss/node': 4.1.16
+ '@tailwindcss/oxide': 4.1.16
+ tailwindcss: 4.1.16
+ vite: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
- '@tailwindcss/vite@4.1.13(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@tailwindcss/vite@4.1.16(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@tailwindcss/node': 4.1.13
- '@tailwindcss/oxide': 4.1.13
- tailwindcss: 4.1.13
- vite: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ '@tailwindcss/node': 4.1.16
+ '@tailwindcss/oxide': 4.1.16
+ tailwindcss: 4.1.16
+ vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
'@tsconfig/svelte@5.0.5': {}
'@types/babel__core@7.20.5':
dependencies:
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
'@types/babel__generator': 7.27.0
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.28.0
'@types/babel__generator@7.27.0':
dependencies:
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
'@types/babel__template@7.4.4':
dependencies:
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
'@types/babel__traverse@7.28.0':
dependencies:
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
'@types/cookie@0.6.0': {}
@@ -3902,64 +3881,48 @@ snapshots:
'@types/lodash@4.17.20': {}
- '@types/node@18.19.127':
+ '@types/node@18.19.130':
dependencies:
undici-types: 5.26.5
- '@types/node@22.18.6':
+ '@types/node@22.18.13':
dependencies:
undici-types: 6.21.0
- '@types/node@24.3.2':
- dependencies:
- undici-types: 7.10.0
-
- '@types/node@24.5.2':
+ '@types/node@24.9.2':
dependencies:
- undici-types: 7.12.0
+ undici-types: 7.16.0
'@types/nprogress@0.2.3': {}
'@types/qs@6.14.0': {}
- '@types/react-dom@19.1.9(@types/react@19.1.13)':
+ '@types/react-dom@19.2.2(@types/react@19.2.2)':
dependencies:
- '@types/react': 19.1.13
+ '@types/react': 19.2.2
- '@types/react@19.1.13':
+ '@types/react@19.2.2':
dependencies:
csstype: 3.1.3
'@types/triple-beam@1.3.5': {}
- '@vitejs/plugin-react@4.7.0(vite@6.3.6(@types/node@24.3.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@babel/core': 7.28.4
- '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
- '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4)
+ '@babel/core': 7.28.5
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5)
'@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
- vite: 6.3.6(@types/node@24.3.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-react@4.7.0(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))':
+ '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))':
dependencies:
- '@babel/core': 7.28.4
- '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
- '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4)
- '@rolldown/pluginutils': 1.0.0-beta.27
- '@types/babel__core': 7.20.5
- react-refresh: 0.17.0
- vite: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
- transitivePeerDependencies:
- - supports-color
-
- '@vitejs/plugin-vue@5.2.4(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0))(vue@3.5.21(typescript@5.9.2))':
- dependencies:
- vite: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
- vue: 3.5.21(typescript@5.9.2)
+ vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vue: 3.5.22(typescript@5.9.3)
'@volar/language-core@2.4.15':
dependencies:
@@ -3973,77 +3936,77 @@ snapshots:
path-browserify: 1.0.1
vscode-uri: 3.1.0
- '@vue/compiler-core@3.5.21':
+ '@vue/compiler-core@3.5.22':
dependencies:
- '@babel/parser': 7.28.4
- '@vue/shared': 3.5.21
+ '@babel/parser': 7.28.5
+ '@vue/shared': 3.5.22
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.1
- '@vue/compiler-dom@3.5.21':
+ '@vue/compiler-dom@3.5.22':
dependencies:
- '@vue/compiler-core': 3.5.21
- '@vue/shared': 3.5.21
+ '@vue/compiler-core': 3.5.22
+ '@vue/shared': 3.5.22
- '@vue/compiler-sfc@3.5.21':
+ '@vue/compiler-sfc@3.5.22':
dependencies:
- '@babel/parser': 7.28.4
- '@vue/compiler-core': 3.5.21
- '@vue/compiler-dom': 3.5.21
- '@vue/compiler-ssr': 3.5.21
- '@vue/shared': 3.5.21
+ '@babel/parser': 7.28.5
+ '@vue/compiler-core': 3.5.22
+ '@vue/compiler-dom': 3.5.22
+ '@vue/compiler-ssr': 3.5.22
+ '@vue/shared': 3.5.22
estree-walker: 2.0.2
- magic-string: 0.30.19
+ magic-string: 0.30.21
postcss: 8.5.6
source-map-js: 1.2.1
- '@vue/compiler-ssr@3.5.21':
+ '@vue/compiler-ssr@3.5.22':
dependencies:
- '@vue/compiler-dom': 3.5.21
- '@vue/shared': 3.5.21
+ '@vue/compiler-dom': 3.5.22
+ '@vue/shared': 3.5.22
'@vue/compiler-vue2@2.7.16':
dependencies:
de-indent: 1.0.2
he: 1.2.0
- '@vue/language-core@2.2.12(typescript@5.9.2)':
+ '@vue/language-core@2.2.12(typescript@5.9.3)':
dependencies:
'@volar/language-core': 2.4.15
- '@vue/compiler-dom': 3.5.21
+ '@vue/compiler-dom': 3.5.22
'@vue/compiler-vue2': 2.7.16
- '@vue/shared': 3.5.21
+ '@vue/shared': 3.5.22
alien-signals: 1.0.13
minimatch: 9.0.5
muggle-string: 0.4.1
path-browserify: 1.0.1
optionalDependencies:
- typescript: 5.9.2
+ typescript: 5.9.3
- '@vue/reactivity@3.5.21':
+ '@vue/reactivity@3.5.22':
dependencies:
- '@vue/shared': 3.5.21
+ '@vue/shared': 3.5.22
- '@vue/runtime-core@3.5.21':
+ '@vue/runtime-core@3.5.22':
dependencies:
- '@vue/reactivity': 3.5.21
- '@vue/shared': 3.5.21
+ '@vue/reactivity': 3.5.22
+ '@vue/shared': 3.5.22
- '@vue/runtime-dom@3.5.21':
+ '@vue/runtime-dom@3.5.22':
dependencies:
- '@vue/reactivity': 3.5.21
- '@vue/runtime-core': 3.5.21
- '@vue/shared': 3.5.21
+ '@vue/reactivity': 3.5.22
+ '@vue/runtime-core': 3.5.22
+ '@vue/shared': 3.5.22
csstype: 3.1.3
- '@vue/server-renderer@3.5.21(vue@3.5.21(typescript@5.9.2))':
+ '@vue/server-renderer@3.5.22(vue@3.5.22(typescript@5.9.3))':
dependencies:
- '@vue/compiler-ssr': 3.5.21
- '@vue/shared': 3.5.21
- vue: 3.5.21(typescript@5.9.2)
+ '@vue/compiler-ssr': 3.5.22
+ '@vue/shared': 3.5.22
+ vue: 3.5.22(typescript@5.9.3)
- '@vue/shared@3.5.21': {}
+ '@vue/shared@3.5.22': {}
'@webassemblyjs/ast@1.14.1':
dependencies:
@@ -4210,7 +4173,7 @@ snapshots:
autosize@6.0.1: {}
- axios@1.12.2:
+ axios@1.13.1:
dependencies:
follow-redirects: 1.15.11
form-data: 4.0.4
@@ -4222,7 +4185,7 @@ snapshots:
balanced-match@1.0.2: {}
- baseline-browser-mapping@2.8.6: {}
+ baseline-browser-mapping@2.8.21: {}
big.js@5.2.2: {}
@@ -4258,13 +4221,13 @@ snapshots:
dependencies:
fill-range: 7.1.1
- browserslist@4.26.2:
+ browserslist@4.27.0:
dependencies:
- baseline-browser-mapping: 2.8.6
- caniuse-lite: 1.0.30001743
- electron-to-chromium: 1.5.223
- node-releases: 2.0.21
- update-browserslist-db: 1.1.3(browserslist@4.26.2)
+ baseline-browser-mapping: 2.8.21
+ caniuse-lite: 1.0.30001751
+ electron-to-chromium: 1.5.243
+ node-releases: 2.0.27
+ update-browserslist-db: 1.1.4(browserslist@4.27.0)
buffer-from@1.1.2: {}
@@ -4286,7 +4249,7 @@ snapshots:
callsites@3.1.0: {}
- caniuse-lite@1.0.30001743: {}
+ caniuse-lite@1.0.30001751: {}
chalk@2.4.2:
dependencies:
@@ -4315,8 +4278,6 @@ snapshots:
dependencies:
readdirp: 4.1.2
- chownr@3.0.0: {}
-
chrome-trace-event@1.0.4: {}
clsx@2.1.1: {}
@@ -4337,30 +4298,30 @@ snapshots:
dependencies:
color-name: 1.1.4
+ color-convert@3.1.2:
+ dependencies:
+ color-name: 2.0.2
+
color-name@1.1.3: {}
color-name@1.1.4: {}
- color-string@1.9.1:
- dependencies:
- color-name: 1.1.4
- simple-swizzle: 0.2.4
+ color-name@2.0.2: {}
- color@3.2.1:
+ color-string@2.1.2:
dependencies:
- color-convert: 1.9.3
- color-string: 1.9.1
+ color-name: 2.0.2
- colorspace@1.1.4:
+ color@5.0.2:
dependencies:
- color: 3.2.1
- text-hex: 1.0.0
+ color-convert: 3.1.2
+ color-string: 2.1.2
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
- commander@14.0.0: {}
+ commander@14.0.1: {}
commander@2.20.3: {}
@@ -4404,7 +4365,7 @@ snapshots:
postcss-modules-values: 4.0.0(postcss@8.5.6)
postcss-value-parser: 4.2.0
schema-utils: 3.3.0
- semver: 7.7.2
+ semver: 7.7.3
webpack: 5.101.3
css-tree@2.3.1:
@@ -4422,16 +4383,12 @@ snapshots:
dependencies:
ms: 2.0.0
- debug@4.4.1(supports-color@5.5.0):
+ debug@4.4.3(supports-color@5.5.0):
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 5.5.0
- debug@4.4.3:
- dependencies:
- ms: 2.1.3
-
dedent-js@1.0.1: {}
deep-is@0.1.4: {}
@@ -4444,9 +4401,9 @@ snapshots:
destroy@1.2.0: {}
- detect-libc@2.1.0: {}
+ detect-libc@2.1.2: {}
- devalue@5.3.2: {}
+ devalue@5.4.2: {}
doctrine@3.0.0:
dependencies:
@@ -4460,7 +4417,7 @@ snapshots:
ee-first@1.1.1: {}
- electron-to-chromium@1.5.223: {}
+ electron-to-chromium@1.5.243: {}
emoji-regex@8.0.0: {}
@@ -4475,7 +4432,7 @@ snapshots:
enhanced-resolve@5.18.3:
dependencies:
graceful-fs: 4.2.11
- tapable: 2.2.3
+ tapable: 2.3.0
enquirer@2.4.1:
dependencies:
@@ -4484,12 +4441,13 @@ snapshots:
entities@4.5.0: {}
- es-check@9.3.1:
+ es-check@9.4.4:
dependencies:
acorn: 8.15.0
acorn-walk: 8.3.4
- browserslist: 4.26.2
- commander: 14.0.0
+ browserslist: 4.27.0
+ commander: 14.0.1
+ fast-brake: 0.1.6
fast-glob: 3.3.3
lilconfig: 3.1.3
source-map: 0.7.6
@@ -4513,9 +4471,9 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.2
- esbuild-node-externals@1.18.0(esbuild@0.25.10):
+ esbuild-node-externals@1.18.0(esbuild@0.25.11):
dependencies:
- esbuild: 0.25.10
+ esbuild: 0.25.11
find-up: 5.0.0
esbuild@0.21.5:
@@ -4544,34 +4502,34 @@ snapshots:
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
- esbuild@0.25.10:
+ esbuild@0.25.11:
optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.10
- '@esbuild/android-arm': 0.25.10
- '@esbuild/android-arm64': 0.25.10
- '@esbuild/android-x64': 0.25.10
- '@esbuild/darwin-arm64': 0.25.10
- '@esbuild/darwin-x64': 0.25.10
- '@esbuild/freebsd-arm64': 0.25.10
- '@esbuild/freebsd-x64': 0.25.10
- '@esbuild/linux-arm': 0.25.10
- '@esbuild/linux-arm64': 0.25.10
- '@esbuild/linux-ia32': 0.25.10
- '@esbuild/linux-loong64': 0.25.10
- '@esbuild/linux-mips64el': 0.25.10
- '@esbuild/linux-ppc64': 0.25.10
- '@esbuild/linux-riscv64': 0.25.10
- '@esbuild/linux-s390x': 0.25.10
- '@esbuild/linux-x64': 0.25.10
- '@esbuild/netbsd-arm64': 0.25.10
- '@esbuild/netbsd-x64': 0.25.10
- '@esbuild/openbsd-arm64': 0.25.10
- '@esbuild/openbsd-x64': 0.25.10
- '@esbuild/openharmony-arm64': 0.25.10
- '@esbuild/sunos-x64': 0.25.10
- '@esbuild/win32-arm64': 0.25.10
- '@esbuild/win32-ia32': 0.25.10
- '@esbuild/win32-x64': 0.25.10
+ '@esbuild/aix-ppc64': 0.25.11
+ '@esbuild/android-arm': 0.25.11
+ '@esbuild/android-arm64': 0.25.11
+ '@esbuild/android-x64': 0.25.11
+ '@esbuild/darwin-arm64': 0.25.11
+ '@esbuild/darwin-x64': 0.25.11
+ '@esbuild/freebsd-arm64': 0.25.11
+ '@esbuild/freebsd-x64': 0.25.11
+ '@esbuild/linux-arm': 0.25.11
+ '@esbuild/linux-arm64': 0.25.11
+ '@esbuild/linux-ia32': 0.25.11
+ '@esbuild/linux-loong64': 0.25.11
+ '@esbuild/linux-mips64el': 0.25.11
+ '@esbuild/linux-ppc64': 0.25.11
+ '@esbuild/linux-riscv64': 0.25.11
+ '@esbuild/linux-s390x': 0.25.11
+ '@esbuild/linux-x64': 0.25.11
+ '@esbuild/netbsd-arm64': 0.25.11
+ '@esbuild/netbsd-x64': 0.25.11
+ '@esbuild/openbsd-arm64': 0.25.11
+ '@esbuild/openbsd-x64': 0.25.11
+ '@esbuild/openharmony-arm64': 0.25.11
+ '@esbuild/sunos-x64': 0.25.11
+ '@esbuild/win32-arm64': 0.25.11
+ '@esbuild/win32-ia32': 0.25.11
+ '@esbuild/win32-x64': 0.25.11
escalade@3.2.0: {}
@@ -4602,7 +4560,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
- debug: 4.4.1(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@5.5.0)
doctrine: 3.0.0
enquirer: 2.4.1
escape-string-regexp: 4.0.0
@@ -4630,7 +4588,7 @@ snapshots:
optionator: 0.9.4
progress: 2.0.3
regexpp: 3.2.0
- semver: 7.7.2
+ semver: 7.7.3
strip-ansi: 6.0.1
strip-json-comments: 3.1.1
table: 6.9.0
@@ -4653,7 +4611,7 @@ snapshots:
dependencies:
estraverse: 5.3.0
- esrap@2.1.0:
+ esrap@2.1.1:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -4713,6 +4671,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ fast-brake@0.1.6: {}
+
fast-deep-equal@3.1.3: {}
fast-glob@3.3.3:
@@ -4909,8 +4869,6 @@ snapshots:
ipaddr.js@1.9.1: {}
- is-arrayish@0.3.4: {}
-
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
@@ -4935,11 +4893,11 @@ snapshots:
jest-worker@27.5.1:
dependencies:
- '@types/node': 24.5.2
+ '@types/node': 24.9.2
merge-stream: 2.0.0
supports-color: 8.1.1
- jiti@2.6.0: {}
+ jiti@2.6.1: {}
js-tokens@4.0.0: {}
@@ -4970,16 +4928,23 @@ snapshots:
kuler@2.0.0: {}
- laravel-vite-plugin@1.3.0(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)):
+ laravel-precognition@0.7.3:
+ dependencies:
+ axios: 1.13.1
+ lodash-es: 4.17.21
+ transitivePeerDependencies:
+ - debug
+
+ laravel-vite-plugin@1.3.0(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)):
dependencies:
picocolors: 1.1.1
- vite: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
vite-plugin-full-reload: 1.2.0
- laravel-vite-plugin@1.3.0(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)):
+ laravel-vite-plugin@1.3.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)):
dependencies:
picocolors: 1.1.1
- vite: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vite-plugin-full-reload: 1.2.0
levn@0.4.1:
@@ -4987,54 +4952,58 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
- lightningcss-darwin-arm64@1.30.1:
+ lightningcss-android-arm64@1.30.2:
+ optional: true
+
+ lightningcss-darwin-arm64@1.30.2:
optional: true
- lightningcss-darwin-x64@1.30.1:
+ lightningcss-darwin-x64@1.30.2:
optional: true
- lightningcss-freebsd-x64@1.30.1:
+ lightningcss-freebsd-x64@1.30.2:
optional: true
- lightningcss-linux-arm-gnueabihf@1.30.1:
+ lightningcss-linux-arm-gnueabihf@1.30.2:
optional: true
- lightningcss-linux-arm64-gnu@1.30.1:
+ lightningcss-linux-arm64-gnu@1.30.2:
optional: true
- lightningcss-linux-arm64-musl@1.30.1:
+ lightningcss-linux-arm64-musl@1.30.2:
optional: true
- lightningcss-linux-x64-gnu@1.30.1:
+ lightningcss-linux-x64-gnu@1.30.2:
optional: true
- lightningcss-linux-x64-musl@1.30.1:
+ lightningcss-linux-x64-musl@1.30.2:
optional: true
- lightningcss-win32-arm64-msvc@1.30.1:
+ lightningcss-win32-arm64-msvc@1.30.2:
optional: true
- lightningcss-win32-x64-msvc@1.30.1:
+ lightningcss-win32-x64-msvc@1.30.2:
optional: true
- lightningcss@1.30.1:
+ lightningcss@1.30.2:
dependencies:
- detect-libc: 2.1.0
+ detect-libc: 2.1.2
optionalDependencies:
- lightningcss-darwin-arm64: 1.30.1
- lightningcss-darwin-x64: 1.30.1
- lightningcss-freebsd-x64: 1.30.1
- lightningcss-linux-arm-gnueabihf: 1.30.1
- lightningcss-linux-arm64-gnu: 1.30.1
- lightningcss-linux-arm64-musl: 1.30.1
- lightningcss-linux-x64-gnu: 1.30.1
- lightningcss-linux-x64-musl: 1.30.1
- lightningcss-win32-arm64-msvc: 1.30.1
- lightningcss-win32-x64-msvc: 1.30.1
+ lightningcss-android-arm64: 1.30.2
+ lightningcss-darwin-arm64: 1.30.2
+ lightningcss-darwin-x64: 1.30.2
+ lightningcss-freebsd-x64: 1.30.2
+ lightningcss-linux-arm-gnueabihf: 1.30.2
+ lightningcss-linux-arm64-gnu: 1.30.2
+ lightningcss-linux-arm64-musl: 1.30.2
+ lightningcss-linux-x64-gnu: 1.30.2
+ lightningcss-linux-x64-musl: 1.30.2
+ lightningcss-win32-arm64-msvc: 1.30.2
+ lightningcss-win32-x64-msvc: 1.30.2
lilconfig@3.1.3: {}
- loader-runner@4.3.0: {}
+ loader-runner@4.3.1: {}
loader-utils@2.0.4:
dependencies:
@@ -5069,11 +5038,11 @@ snapshots:
dependencies:
yallist: 3.1.1
- magic-string@0.30.19:
+ magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
- marked@16.3.0: {}
+ marked@16.4.1: {}
math-intrinsics@1.1.0: {}
@@ -5116,12 +5085,6 @@ snapshots:
minimist@1.2.8: {}
- minipass@7.1.2: {}
-
- minizlib@3.1.0:
- dependencies:
- minipass: 7.1.2
-
mkdirp@0.5.6:
dependencies:
minimist: 1.2.8
@@ -5156,16 +5119,16 @@ snapshots:
neo-async@2.6.2: {}
- node-releases@2.0.21: {}
+ node-releases@2.0.27: {}
nodemon@3.1.10:
dependencies:
chokidar: 3.6.0
- debug: 4.4.1(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@5.5.0)
ignore-by-default: 1.0.1
minimatch: 3.1.2
pstree.remy: 1.1.8
- semver: 7.7.2
+ semver: 7.7.3
simple-update-notifier: 2.0.0
supports-color: 5.5.0
touch: 3.1.1
@@ -5247,11 +5210,11 @@ snapshots:
picomatch@4.0.3: {}
- playwright-core@1.55.1: {}
+ playwright-core@1.56.1: {}
- playwright@1.55.1:
+ playwright@1.56.1:
dependencies:
- playwright-core: 1.55.1
+ playwright-core: 1.56.1
optionalDependencies:
fsevents: 2.3.2
@@ -5296,24 +5259,24 @@ snapshots:
prelude-ls@1.2.1: {}
- prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.2)(vue-tsc@2.2.12(typescript@5.9.2)):
+ prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)):
dependencies:
prettier: 3.6.2
- typescript: 5.9.2
+ typescript: 5.9.3
optionalDependencies:
- vue-tsc: 2.2.12(typescript@5.9.2)
+ vue-tsc: 2.2.12(typescript@5.9.3)
- prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.5):
+ prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.0):
dependencies:
prettier: 3.6.2
- svelte: 5.39.5
+ svelte: 5.43.0
- prettier-plugin-tailwindcss@0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.2)(vue-tsc@2.2.12(typescript@5.9.2)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.5))(prettier@3.6.2):
+ prettier-plugin-tailwindcss@0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.0))(prettier@3.6.2):
dependencies:
prettier: 3.6.2
optionalDependencies:
- prettier-plugin-organize-imports: 4.3.0(prettier@3.6.2)(typescript@5.9.2)(vue-tsc@2.2.12(typescript@5.9.2))
- prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.39.5)
+ prettier-plugin-organize-imports: 4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3))
+ prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.43.0)
prettier@3.6.2: {}
@@ -5359,14 +5322,14 @@ snapshots:
iconv-lite: 0.4.24
unpipe: 1.0.0
- react-dom@19.1.1(react@19.1.1):
+ react-dom@19.2.0(react@19.2.0):
dependencies:
- react: 19.1.1
- scheduler: 0.26.0
+ react: 19.2.0
+ scheduler: 0.27.0
react-refresh@0.17.0: {}
- react@19.1.1: {}
+ react@19.2.0: {}
readable-stream@3.6.2:
dependencies:
@@ -5392,32 +5355,32 @@ snapshots:
dependencies:
glob: 7.2.3
- rollup@4.52.2:
+ rollup@4.52.5:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.52.2
- '@rollup/rollup-android-arm64': 4.52.2
- '@rollup/rollup-darwin-arm64': 4.52.2
- '@rollup/rollup-darwin-x64': 4.52.2
- '@rollup/rollup-freebsd-arm64': 4.52.2
- '@rollup/rollup-freebsd-x64': 4.52.2
- '@rollup/rollup-linux-arm-gnueabihf': 4.52.2
- '@rollup/rollup-linux-arm-musleabihf': 4.52.2
- '@rollup/rollup-linux-arm64-gnu': 4.52.2
- '@rollup/rollup-linux-arm64-musl': 4.52.2
- '@rollup/rollup-linux-loong64-gnu': 4.52.2
- '@rollup/rollup-linux-ppc64-gnu': 4.52.2
- '@rollup/rollup-linux-riscv64-gnu': 4.52.2
- '@rollup/rollup-linux-riscv64-musl': 4.52.2
- '@rollup/rollup-linux-s390x-gnu': 4.52.2
- '@rollup/rollup-linux-x64-gnu': 4.52.2
- '@rollup/rollup-linux-x64-musl': 4.52.2
- '@rollup/rollup-openharmony-arm64': 4.52.2
- '@rollup/rollup-win32-arm64-msvc': 4.52.2
- '@rollup/rollup-win32-ia32-msvc': 4.52.2
- '@rollup/rollup-win32-x64-gnu': 4.52.2
- '@rollup/rollup-win32-x64-msvc': 4.52.2
+ '@rollup/rollup-android-arm-eabi': 4.52.5
+ '@rollup/rollup-android-arm64': 4.52.5
+ '@rollup/rollup-darwin-arm64': 4.52.5
+ '@rollup/rollup-darwin-x64': 4.52.5
+ '@rollup/rollup-freebsd-arm64': 4.52.5
+ '@rollup/rollup-freebsd-x64': 4.52.5
+ '@rollup/rollup-linux-arm-gnueabihf': 4.52.5
+ '@rollup/rollup-linux-arm-musleabihf': 4.52.5
+ '@rollup/rollup-linux-arm64-gnu': 4.52.5
+ '@rollup/rollup-linux-arm64-musl': 4.52.5
+ '@rollup/rollup-linux-loong64-gnu': 4.52.5
+ '@rollup/rollup-linux-ppc64-gnu': 4.52.5
+ '@rollup/rollup-linux-riscv64-gnu': 4.52.5
+ '@rollup/rollup-linux-riscv64-musl': 4.52.5
+ '@rollup/rollup-linux-s390x-gnu': 4.52.5
+ '@rollup/rollup-linux-x64-gnu': 4.52.5
+ '@rollup/rollup-linux-x64-musl': 4.52.5
+ '@rollup/rollup-openharmony-arm64': 4.52.5
+ '@rollup/rollup-win32-arm64-msvc': 4.52.5
+ '@rollup/rollup-win32-ia32-msvc': 4.52.5
+ '@rollup/rollup-win32-x64-gnu': 4.52.5
+ '@rollup/rollup-win32-x64-msvc': 4.52.5
fsevents: 2.3.3
run-parallel@1.2.0:
@@ -5434,7 +5397,7 @@ snapshots:
safer-buffer@2.1.2: {}
- scheduler@0.26.0: {}
+ scheduler@0.27.0: {}
schema-utils@3.3.0:
dependencies:
@@ -5442,7 +5405,7 @@ snapshots:
ajv: 6.12.6
ajv-keywords: 3.5.2(ajv@6.12.6)
- schema-utils@4.3.2:
+ schema-utils@4.3.3:
dependencies:
'@types/json-schema': 7.0.15
ajv: 8.17.1
@@ -5453,7 +5416,7 @@ snapshots:
semver@6.3.1: {}
- semver@7.7.2: {}
+ semver@7.7.3: {}
send@0.19.0:
dependencies:
@@ -5486,7 +5449,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- set-cookie-parser@2.7.1: {}
+ set-cookie-parser@2.7.2: {}
setprototypeof@1.2.0: {}
@@ -5524,13 +5487,9 @@ snapshots:
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
- simple-swizzle@0.2.4:
- dependencies:
- is-arrayish: 0.3.4
-
simple-update-notifier@2.0.0:
dependencies:
- semver: 7.7.2
+ semver: 7.7.3
sirv@3.0.2:
dependencies:
@@ -5591,19 +5550,7 @@ snapshots:
dependencies:
has-flag: 4.0.0
- svelte-check@4.3.1(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.2):
- dependencies:
- '@jridgewell/trace-mapping': 0.3.31
- chokidar: 4.0.3
- fdir: 6.5.0(picomatch@4.0.3)
- picocolors: 1.1.1
- sade: 1.8.1
- svelte: 4.2.20
- typescript: 5.9.2
- transitivePeerDependencies:
- - picomatch
-
- svelte-check@4.3.2(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.2):
+ svelte-check@4.3.3(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
chokidar: 4.0.3
@@ -5611,19 +5558,19 @@ snapshots:
picocolors: 1.1.1
sade: 1.8.1
svelte: 4.2.20
- typescript: 5.9.2
+ typescript: 5.9.3
transitivePeerDependencies:
- picomatch
- svelte-check@4.3.2(picomatch@4.0.3)(svelte@5.39.5)(typescript@5.9.2):
+ svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.43.0)(typescript@5.9.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
chokidar: 4.0.3
fdir: 6.5.0(picomatch@4.0.3)
picocolors: 1.1.1
sade: 1.8.1
- svelte: 5.39.5
- typescript: 5.9.2
+ svelte: 5.43.0
+ typescript: 5.9.3
transitivePeerDependencies:
- picomatch
@@ -5631,12 +5578,12 @@ snapshots:
dependencies:
svelte: 4.2.20
- svelte2tsx@0.7.44(svelte@4.2.20)(typescript@5.9.2):
+ svelte2tsx@0.7.45(svelte@4.2.20)(typescript@5.9.3):
dependencies:
dedent-js: 1.0.1
scule: 1.3.0
svelte: 4.2.20
- typescript: 5.9.2
+ typescript: 5.9.3
svelte@4.2.20:
dependencies:
@@ -5652,24 +5599,24 @@ snapshots:
estree-walker: 3.0.3
is-reference: 3.0.3
locate-character: 3.0.0
- magic-string: 0.30.19
+ magic-string: 0.30.21
periscopic: 3.1.0
- svelte@5.39.5:
+ svelte@5.43.0:
dependencies:
'@jridgewell/remapping': 2.3.5
'@jridgewell/sourcemap-codec': 1.5.5
- '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
+ '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0)
'@types/estree': 1.0.8
acorn: 8.15.0
aria-query: 5.3.2
axobject-query: 4.1.0
clsx: 2.1.1
esm-env: 1.2.2
- esrap: 2.1.0
+ esrap: 2.1.1
is-reference: 3.0.3
locate-character: 3.0.0
- magic-string: 0.30.19
+ magic-string: 0.30.21
zimmerframe: 1.1.4
table@6.9.0:
@@ -5680,23 +5627,15 @@ snapshots:
string-width: 4.2.3
strip-ansi: 6.0.1
- tailwindcss@4.1.13: {}
+ tailwindcss@4.1.16: {}
- tapable@2.2.3: {}
-
- tar@7.5.1:
- dependencies:
- '@isaacs/fs-minipass': 4.0.1
- chownr: 3.0.0
- minipass: 7.1.2
- minizlib: 3.1.0
- yallist: 5.0.0
+ tapable@2.3.0: {}
terser-webpack-plugin@5.3.14(webpack@5.101.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
- schema-utils: 4.3.2
+ schema-utils: 4.3.3
serialize-javascript: 6.0.2
terser: 5.44.0
webpack: 5.101.3
@@ -5746,7 +5685,7 @@ snapshots:
typescript@4.9.5: {}
- typescript@5.9.2: {}
+ typescript@5.9.3: {}
undefsafe@2.0.5: {}
@@ -5754,15 +5693,13 @@ snapshots:
undici-types@6.21.0: {}
- undici-types@7.10.0: {}
-
- undici-types@7.12.0: {}
+ undici-types@7.16.0: {}
unpipe@1.0.0: {}
- update-browserslist-db@1.1.3(browserslist@4.26.2):
+ update-browserslist-db@1.1.4(browserslist@4.27.0):
dependencies:
- browserslist: 4.26.2
+ browserslist: 4.27.0
escalade: 3.2.0
picocolors: 1.1.1
@@ -5783,72 +5720,57 @@ snapshots:
picocolors: 1.1.1
picomatch: 2.3.1
- vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0):
+ vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0):
dependencies:
esbuild: 0.21.5
postcss: 8.5.6
- rollup: 4.52.2
+ rollup: 4.52.5
optionalDependencies:
- '@types/node': 24.5.2
+ '@types/node': 24.9.2
fsevents: 2.3.3
- lightningcss: 1.30.1
+ lightningcss: 1.30.2
terser: 5.44.0
- vite@6.3.6(@types/node@24.3.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0):
+ vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0):
dependencies:
- esbuild: 0.25.10
+ esbuild: 0.25.11
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
- rollup: 4.52.2
+ rollup: 4.52.5
tinyglobby: 0.2.15
optionalDependencies:
- '@types/node': 24.3.2
+ '@types/node': 24.9.2
fsevents: 2.3.3
- jiti: 2.6.0
- lightningcss: 1.30.1
+ jiti: 2.6.1
+ lightningcss: 1.30.2
terser: 5.44.0
- vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0):
- dependencies:
- esbuild: 0.25.10
- fdir: 6.5.0(picomatch@4.0.3)
- picomatch: 4.0.3
- postcss: 8.5.6
- rollup: 4.52.2
- tinyglobby: 0.2.15
- optionalDependencies:
- '@types/node': 24.5.2
- fsevents: 2.3.3
- jiti: 2.6.0
- lightningcss: 1.30.1
- terser: 5.44.0
-
- vitefu@0.2.5(vite@5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)):
+ vitefu@0.2.5(vite@5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)):
optionalDependencies:
- vite: 5.4.20(@types/node@24.5.2)(lightningcss@1.30.1)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.9.2)(lightningcss@1.30.2)(terser@5.44.0)
- vitefu@1.1.1(vite@6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)):
+ vitefu@1.1.1(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)):
optionalDependencies:
- vite: 6.3.6(@types/node@24.5.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)
+ vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vscode-uri@3.1.0: {}
- vue-tsc@2.2.12(typescript@5.9.2):
+ vue-tsc@2.2.12(typescript@5.9.3):
dependencies:
'@volar/typescript': 2.4.15
- '@vue/language-core': 2.2.12(typescript@5.9.2)
- typescript: 5.9.2
+ '@vue/language-core': 2.2.12(typescript@5.9.3)
+ typescript: 5.9.3
- vue@3.5.21(typescript@5.9.2):
+ vue@3.5.22(typescript@5.9.3):
dependencies:
- '@vue/compiler-dom': 3.5.21
- '@vue/compiler-sfc': 3.5.21
- '@vue/runtime-dom': 3.5.21
- '@vue/server-renderer': 3.5.21(vue@3.5.21(typescript@5.9.2))
- '@vue/shared': 3.5.21
+ '@vue/compiler-dom': 3.5.22
+ '@vue/compiler-sfc': 3.5.22
+ '@vue/runtime-dom': 3.5.22
+ '@vue/server-renderer': 3.5.22(vue@3.5.22(typescript@5.9.3))
+ '@vue/shared': 3.5.22
optionalDependencies:
- typescript: 5.9.2
+ typescript: 5.9.3
watchpack@2.4.4:
dependencies:
@@ -5867,7 +5789,7 @@ snapshots:
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.15.0
acorn-import-phases: 1.0.4(acorn@8.15.0)
- browserslist: 4.26.2
+ browserslist: 4.27.0
chrome-trace-event: 1.0.4
enhanced-resolve: 5.18.3
es-module-lexer: 1.7.0
@@ -5876,11 +5798,11 @@ snapshots:
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
json-parse-even-better-errors: 2.3.1
- loader-runner: 4.3.0
+ loader-runner: 4.3.1
mime-types: 2.1.35
neo-async: 2.6.2
- schema-utils: 4.3.2
- tapable: 2.2.3
+ schema-utils: 4.3.3
+ tapable: 2.3.0
terser-webpack-plugin: 5.3.14(webpack@5.101.3)
watchpack: 2.4.4
webpack-sources: 3.3.3
@@ -5902,7 +5824,7 @@ snapshots:
winston@3.17.0:
dependencies:
'@colors/colors': 1.6.0
- '@dabh/diagnostics': 2.0.3
+ '@dabh/diagnostics': 2.0.8
async: 3.2.6
is-stream: 2.0.1
logform: 2.7.0
@@ -5921,8 +5843,6 @@ snapshots:
yallist@3.1.1: {}
- yallist@5.0.0: {}
-
yocto-queue@0.1.0: {}
zimmerframe@1.1.4: {}
diff --git a/tests/app/package.json b/tests/app/package.json
index 1cc66b0b7..ffbefe448 100644
--- a/tests/app/package.json
+++ b/tests/app/package.json
@@ -5,11 +5,11 @@
"serve:watch": "nodemon server.js"
},
"devDependencies": {
- "body-parser": "^1.19.0",
- "css-loader": "^5.0.1",
- "eslint": "^7.17.0",
- "express": "^4.21.1",
+ "body-parser": "^1.20.3",
+ "css-loader": "^5.2.7",
+ "eslint": "^7.32.0",
+ "express": "^4.21.2",
"multer": "^2.0.2",
- "nodemon": "^3.0.0"
+ "nodemon": "^3.1.10"
}
}
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 9d258e21d..1ea756e29 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1746,7 +1746,7 @@ test.describe('Form Component', () => {
await expect(page.locator('#any-touched')).toHaveText('Form has touched fields')
})
- test('validate with specific field works independently of touched state', async ({ page }) => {
+ test('validating a specific field also validates previously touched inputs', async ({ page }) => {
await page.goto('/form-component/precognition-methods')
await page.fill('input[name="name"]', 'ab')
@@ -1758,7 +1758,7 @@ test.describe('Form Component', () => {
await expect(page.getByText('Validating...')).not.toBeVisible()
await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
- await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
})
test('validate with array of fields validates multiple fields', async ({ page }) => {
@@ -1806,23 +1806,6 @@ test.describe('Form Component', () => {
await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
})
- test('touching one field and validating another does not validate the touched field', async ({ page }) => {
- await page.goto('/form-component/precognition-methods')
-
- await page.fill('input[name="name"]', 'ab')
- await page.locator('input[name="name"]').blur()
-
- await page.fill('input[name="email"]', 'x')
-
- await page.getByRole('button', { name: 'Validate Name', exact: true }).click()
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
- await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
- })
-
test('does not submit files by default', async ({ page }) => {
await page.goto('/form-component/precognition-files')
@@ -1923,21 +1906,6 @@ test.describe('Form Component', () => {
}
})
- test('onException handles non-422 errors during validation', async ({ page }) => {
- await page.goto('/form-component/precognition-callbacks')
-
- await page.fill('#name-input', 'John')
-
- // Trigger validation that will return 500 error
- await page.click('button:has-text("Validate with Exception Handler")')
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Exception should be caught and displayed
- await expect(page.getByText(/Exception caught:/)).toBeVisible()
- })
-
test('sends custom headers with validation requests', async ({ page }) => {
await page.goto('/form-component/precognition-headers')
@@ -1976,24 +1944,6 @@ test.describe('Form Component', () => {
expect(cancelledRequestError).toBe('net::ERR_ABORTED')
})
- test('cancelValidation() cancels in-flight validation and resets validating state', async ({ page }) => {
- await page.goto('/form-component/precognition-cancel')
-
- requests.listenForFailed(page)
-
- await page.fill('#manual-cancel-name-input', 'ab')
- await page.locator('#manual-cancel-name-input').blur()
- await expect(page.getByText('Validating...')).toBeVisible()
-
- await page.getByText('Cancel Validation').click()
- await expect(page.getByText('Validating...')).not.toBeVisible()
- await page.waitForTimeout(100)
- expect(requests.failed).toHaveLength(1)
-
- const cancelledRequestError = await requests.failed[0].failure()?.errorText
- expect(cancelledRequestError).toBe('net::ERR_ABORTED')
- })
-
test('defaults() updates validator data as well', async ({ page }) => {
await page.goto('/form-component/precognition-defaults')
From 8425e8de2153e58cf4b3136a017887523197b1af Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 12:18:04 +0100
Subject: [PATCH 058/145] React
---
packages/react/src/Form.ts | 195 ++++++++++++++++---------------------
1 file changed, 84 insertions(+), 111 deletions(-)
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 555a662cd..4564aa556 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -1,17 +1,23 @@
import {
+ Errors,
FormComponentProps,
FormComponentRef,
FormComponentSlotProps,
- FormComponentValidateOptions,
FormDataConvertible,
formDataToObject,
isUrlMethodPair,
mergeDataIntoQueryString,
Method,
resetFormFields,
- usePrecognition,
VisitOptions,
} from '@inertiajs/core'
+import {
+ createValidator,
+ NamedInputEvent,
+ toSimpleValidationErrors,
+ ValidationConfig,
+ Validator,
+} from 'laravel-precognition'
import { isEqual } from 'lodash-es'
import React, {
createElement,
@@ -85,18 +91,10 @@ const Form = forwardRef(
const defaultData = useRef(new FormData())
const [validating, setValidating] = useState(false)
- const [validated, setValidated] = useState([])
+ const [valid, setValid] = useState([])
const [touched, setTouched] = useState([])
- const validator = useMemo(
- () =>
- usePrecognition({
- timeout: validateTimeout,
- onStart: () => setValidating(true),
- onFinish: () => setValidating(false),
- }),
- [],
- )
+ const [validator, setValidator] = useState(null)
const getFormData = (): FormData => new FormData(formElement.current)
@@ -119,6 +117,25 @@ const Form = forwardRef(
setIsDirty(event.type === 'reset' ? false : !isEqual(getData(), formDataToObject(defaultData.current))),
)
+ const clearErrors = (...names: string[]) => {
+ form.clearErrors(...names)
+
+ if (validator) {
+ if (names.length === 0) {
+ validator.setErrors({})
+ } else {
+ names.forEach(validator.forgetError)
+ }
+ }
+
+ return form
+ }
+
+ const getTransformedData = (): Record => {
+ const [_url, data] = getUrlAndData()
+ return transform(data)
+ }
+
useEffect(() => {
defaultData.current = getFormData()
@@ -126,42 +143,65 @@ const Form = forwardRef(
formEvents.forEach((e) => formElement.current.addEventListener(e, updateDirtyState))
- return () => formEvents.forEach((e) => formElement.current?.removeEventListener(e, updateDirtyState))
- }, [])
-
- useEffect(() => {
- validator.validateFiles(validateFiles)
- }, [validateFiles, validator])
+ // Initialize validator
+ const newValidator = createValidator(
+ (client) =>
+ client[resolvedMethod](getUrlAndData()[0], getTransformedData(), {
+ headers,
+ }),
+ getTransformedData(),
+ )
+ .on('validatingChanged', () => {
+ setValidating(newValidator.validating())
+ })
+ .on('validatedChanged', () => {
+ setValid(newValidator.valid())
+ })
+ .on('touchedChanged', () => {
+ setTouched(newValidator.touched())
+ })
+ .on('errorsChanged', () => {
+ form.clearErrors()
+
+ form.setError(
+ (simpleValidationErrors
+ ? toSimpleValidationErrors(newValidator.errors())
+ : newValidator.errors()) as Errors,
+ )
+
+ setValid(newValidator.valid())
+ })
+
+ newValidator.setTimeout(validateTimeout)
+
+ if (validateFiles) {
+ newValidator.validateFiles()
+ }
- useEffect(() => {
- validator.setTimeout(validateTimeout)
- }, [validateTimeout, validator])
+ setValidator(newValidator)
- useEffect(() => {
- updateDataOnValidator()
+ return () => {
+ formEvents.forEach((e) => formElement.current?.removeEventListener(e, updateDirtyState))
+ newValidator?.reset()
+ }
}, [])
- const updateDataOnValidator = () => {
- try {
- // This might fail if the component is already unmounted but this function
- // is called after navigating away after a form submission.
- validator.setOldData(transform(getData()))
- } catch {}
- }
+ useEffect(() => {
+ if (validator) {
+ validator.setTimeout(validateTimeout)
+ }
+ }, [validateTimeout, validator])
const reset = (...fields: string[]) => {
resetFormFields(formElement.current, defaultData.current, fields)
- updateDataOnValidator()
- if (fields.length === 0) {
- setTouched([])
- } else {
- setTouched((prev) => prev.filter((field) => !fields.includes(field)))
+ if (validator) {
+ validator.reset(...fields)
}
}
const resetAndClearErrors = (...fields: string[]) => {
- form.clearErrors(...fields)
+ clearErrors(...fields)
reset(...fields)
}
@@ -217,70 +257,12 @@ const Form = forwardRef(
const defaults = () => {
defaultData.current = getFormData()
setIsDirty(false)
- updateDataOnValidator()
}
- const validate = (
- only?: string | string[] | FormComponentValidateOptions,
- maybeOptions?: FormComponentValidateOptions,
- ) => {
- let fields: string[]
- let options: FormComponentValidateOptions = {}
-
- if (typeof only === 'object' && !Array.isArray(only)) {
- // Called as validate({ only: [...], onSuccess, onError, onFinish })
- const onlyFields = only.only
- fields = onlyFields === undefined ? touched : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
- options = only
- } else {
- // Called as validate('field') or validate(['field1', 'field2']) or validate('field', {options})
- fields = only === undefined ? touched : Array.isArray(only) ? only : [only]
- options = maybeOptions || {}
+ const touch = (...fields: string[]) => {
+ if (validator) {
+ validator.touch(fields)
}
-
- // We're not using the data object from this method as it might be empty
- // on GET requests, and we still want to pass a data object to the
- // validator so it knows the current state of the form.
- const [url] = getUrlAndData()
-
- validator.validate({
- url,
- method: resolvedMethod,
- data: transform(getData()),
- only: fields,
- errorBag,
- headers,
- simpleValidationErrors,
- onBefore: options.onBefore,
- onPrecognitionSuccess: () => {
- setValidated((prev) => [...prev, ...fields])
- form.clearErrors(...fields)
- options.onSuccess?.()
- },
- onValidationError: (errors) => {
- setValidated((prev) => [...prev, ...fields])
-
- const validFields = fields.filter((field) => errors[field] === undefined)
-
- if (validFields.length) {
- form.clearErrors(...validFields)
- }
-
- form.setError({ ...form.errors, ...errors })
- options.onError?.(errors)
- },
- onException: options.onException,
- onFinish: () => {
- options.onFinish?.()
- },
- })
- }
-
- const touch = (field: string | string[]) => {
- const fields = Array.isArray(field) ? field : [field]
-
- // Use Set to avoid duplicates
- setTouched((prev) => [...new Set([...prev, ...fields])])
}
const isTouched = (field?: string): boolean => {
@@ -299,7 +281,7 @@ const Form = forwardRef(
wasSuccessful: form.wasSuccessful,
recentlySuccessful: form.recentlySuccessful,
isDirty,
- clearErrors: form.clearErrors,
+ clearErrors,
resetAndClearErrors,
setError: form.setError,
reset,
@@ -308,24 +290,15 @@ const Form = forwardRef(
// Precognition
validating,
- valid: (field: string) => validated.includes(field) && form.errors[field] === undefined,
+ valid: (field: string) => valid.includes(field),
invalid: (field: string) => form.errors[field] !== undefined,
- validate,
+ validate: (input?: string | NamedInputEvent | ValidationConfig, value?: unknown, config?: ValidationConfig) =>
+ validator?.validate(input, value, config),
touch,
touched: isTouched,
})
- useImperativeHandle(ref, exposed, [
- form,
- isDirty,
- submit,
- validating,
- validated,
- touched,
- validate,
- touch,
- validator,
- ])
+ useImperativeHandle(ref, exposed, [form, isDirty, submit, validating, valid, touched, touch, validator])
return createElement(
'form',
From 622f23702813a42cf9a4d39bc6374a2ce22a5360 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 13:48:35 +0100
Subject: [PATCH 059/145] Svelte
---
packages/react/src/Form.ts | 1 -
packages/svelte/src/components/Form.svelte | 169 ++++++++----------
.../PrecognitionCallbacks.svelte | 2 +-
3 files changed, 75 insertions(+), 97 deletions(-)
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 4564aa556..01a8b653d 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -182,7 +182,6 @@ const Form = forwardRef(
return () => {
formEvents.forEach((e) => formElement.current?.removeEventListener(e, updateDirtyState))
- newValidator?.reset()
}
}, [])
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 25db4c033..bf0238c7a 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -6,11 +6,16 @@
type Errors,
type FormComponentProps,
type FormDataConvertible,
- type FormComponentValidateOptions,
type VisitOptions,
isUrlMethodPair,
- usePrecognition,
} from '@inertiajs/core'
+ import {
+ createValidator,
+ toSimpleValidationErrors,
+ type NamedInputEvent,
+ type ValidationConfig,
+ type Validator,
+ } from 'laravel-precognition'
import { isEqual } from 'lodash-es'
import { onMount } from 'svelte'
import useForm from '../useForm'
@@ -51,18 +56,9 @@
let defaultData: FormData = new FormData()
let validating = false
- let validated: string[] = []
+ let validFields: string[] = []
let touchedFields: string[] = []
-
- const validator = usePrecognition({
- timeout: validateTimeout,
- onStart: () => {
- validating = true
- },
- onFinish: () => {
- validating = false
- },
- })
+ let validator: Validator | null = null
$: _method = isUrlMethodPair(action) ? action.method : (method.toLowerCase() as FormComponentProps['method'])
$: _action = isUrlMethodPair(action) ? action.url : action
@@ -86,6 +82,11 @@
isDirty = event.type === 'reset' ? false : !isEqual(getData(), formDataToObject(defaultData))
}
+ function getTransformedData(): Record {
+ const [_url, data] = getUrlAndData()
+ return transform!(data)
+ }
+
export function submit() {
const [url, _data] = getUrlAndData()
@@ -156,33 +157,29 @@
}
}
- function updateDataOnValidator() {
- try {
- // This might fail if the component is already unmounted but this function
- // is called after navigating away after a form submission.
- validator.setOldData(transform(getData()))
- } catch {}
- }
-
export function reset(...fields: string[]) {
resetFormFields(formElement, defaultData, fields)
- updateDataOnValidator()
- if (fields.length === 0) {
- touchedFields = []
- } else {
- touchedFields = touchedFields.filter((field) => !fields.includes(field))
+ if (validator) {
+ validator.reset(...fields)
}
}
export function clearErrors(...fields: string[]) {
// @ts-expect-error
$form.clearErrors(...fields)
+
+ if (validator) {
+ if (fields.length === 0) {
+ validator.setErrors({})
+ } else {
+ fields.forEach(validator.forgetError)
+ }
+ }
}
export function resetAndClearErrors(...fields: string[]) {
- // @ts-expect-error
- $form.clearErrors(...fields)
+ clearErrors(...fields)
reset(...fields)
}
@@ -198,72 +195,22 @@
export function defaults() {
defaultData = getFormData()
isDirty = false
- updateDataOnValidator()
}
export function validate(
- only?: string | string[] | FormComponentValidateOptions,
- maybeOptions?: FormComponentValidateOptions,
+ input?: string | NamedInputEvent | ValidationConfig,
+ value?: unknown,
+ config?: ValidationConfig,
) {
- let fields: string[]
- let options: FormComponentValidateOptions = {}
-
- if (typeof only === 'object' && !Array.isArray(only)) {
- // Called as validate({ only: [...], onSuccess, onError, onFinish })
- const onlyFields = only.only
- fields = onlyFields === undefined ? touchedFields : Array.isArray(onlyFields) ? onlyFields : [onlyFields]
- options = only
- } else {
- // Called as validate('field') or validate(['field1', 'field2']) or validate('field', {options})
- fields = only === undefined ? touchedFields : Array.isArray(only) ? only : [only]
- options = maybeOptions || {}
+ if (validator) {
+ return validator.validate(input, value, config)
}
-
- // We're not using the data object from this method as it might be empty
- // on GET requests, and we still want to pass a data object to the
- // validator so it knows the current state of the form.
- const [url] = getUrlAndData()
-
- validator.validate({
- url,
- method: _method,
- data: transform(getData()),
- only: fields,
- errorBag,
- headers,
- simpleValidationErrors,
- onBefore: options.onBefore,
- onPrecognitionSuccess: () => {
- validated = [...validated, ...fields]
- clearErrors(...fields)
- options.onSuccess?.()
- },
- onValidationError: (errors) => {
- validated = [...validated, ...fields]
-
- const validFields = fields.filter((field) => errors[field] === undefined)
-
- if (validFields.length) {
- clearErrors(...validFields)
- }
-
- // Merge current errors with new errors
- const mergedErrors = { ...$form.errors, ...errors }
- setError(mergedErrors)
- options.onError?.(errors)
- },
- onException: options.onException,
- onFinish: () => {
- options.onFinish?.()
- },
- })
}
- export function touch(field: string | string[]) {
- const fields = Array.isArray(field) ? field : [field]
-
- // Use Set to avoid duplicates
- touchedFields = [...new Set([...touchedFields, ...fields])]
+ export function touch(...fields: string[]) {
+ if (validator) {
+ validator.touch(fields)
+ }
}
export function touched(field?: string): boolean {
@@ -275,36 +222,68 @@
}
export function valid(field: string): boolean {
- return validated.includes(field) && $form.errors[field] === undefined
+ return validFields.includes(field)
}
export function invalid(field: string): boolean {
return $form.errors[field] !== undefined
}
-
onMount(() => {
defaultData = getFormData()
const formEvents = ['input', 'change', 'reset']
formEvents.forEach((e) => formElement.addEventListener(e, updateDirtyState))
- updateDataOnValidator()
- validator.validateFiles(validateFiles)
- validator.setTimeout(validateTimeout)
+ // Initialize validator
+ validator = createValidator(
+ (client) =>
+ client[_method!](getUrlAndData()[0], getTransformedData(), {
+ headers,
+ }),
+ getTransformedData(),
+ )
+ .on('validatingChanged', () => {
+ validating = validator!.validating()
+ })
+ .on('validatedChanged', () => {
+ validFields = validator!.valid()
+ })
+ .on('touchedChanged', () => {
+ touchedFields = validator!.touched()
+ })
+ .on('errorsChanged', () => {
+ // Clear form errors first
+ $form.clearErrors()
+
+ // Set new errors
+ const errors = simpleValidationErrors ? toSimpleValidationErrors(validator!.errors()) : validator!.errors()
+
+ $form.setError(errors as Errors)
+ validFields = validator!.valid()
+ })
+
+ validator.setTimeout(validateTimeout!)
+
+ if (validateFiles) {
+ validator.validateFiles()
+ }
return () => {
formEvents.forEach((e) => formElement?.removeEventListener(e, updateDirtyState))
}
})
- $: validator.validateFiles(validateFiles)
- $: validator.setTimeout(validateTimeout)
+ $: {
+ if (validator) {
+ validator.setTimeout(validateTimeout!)
+ }
+ }
$: slotErrors = $form.errors as Errors
// Create reactive slot props that update when state changes
- $: validMethod = (field: string) => validated.includes(field) && slotErrors[field] === undefined
+ $: validMethod = (field: string) => validFields.includes(field)
$: invalidMethod = (field: string) => slotErrors[field] !== undefined
$: touchedMethod = (field?: string) =>
typeof field === 'string' ? touchedFields.includes(field) : touchedFields.length > 0
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
index 1237f4b37..af497791a 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
@@ -61,7 +61,7 @@
errorCalled = false
finishCalled = false
validate({
- onError: () => {
+ onValidationError: () => {
errorCalled = true
},
onFinish: () => {
From 56bc4416907113325ab7ac02ed91c6bfa62c05b9 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 16:26:46 +0100
Subject: [PATCH 060/145] fixes
---
packages/core/src/types.ts | 2 +-
packages/react/package.json | 2 +-
packages/react/src/Form.ts | 6 ++----
.../FormComponent/PrecognitionBeforeValidation.tsx | 10 +++++-----
.../Pages/FormComponent/PrecognitionCallbacks.tsx | 7 -------
.../Pages/FormComponent/PrecognitionDefaults.tsx | 3 ++-
.../Pages/FormComponent/PrecognitionFiles.tsx | 4 ++--
.../Pages/FormComponent/PrecognitionMethods.tsx | 4 ++--
packages/react/test-app/package.json | 2 +-
packages/svelte/package.json | 2 +-
packages/svelte/src/components/Form.svelte | 13 ++++---------
.../FormComponent/PrecognitionAllErrors.svelte | 4 ++--
.../PrecognitionBeforeValidation.svelte | 6 +++---
.../FormComponent/PrecognitionCallbacks.svelte | 7 -------
.../Pages/FormComponent/PrecognitionDefaults.svelte | 3 ++-
.../Pages/FormComponent/PrecognitionFiles.svelte | 3 +--
.../Pages/FormComponent/PrecognitionMethods.svelte | 4 ++--
packages/svelte/test-app/package.json | 2 +-
packages/vue3/package.json | 2 +-
packages/vue3/src/form.ts | 2 +-
.../FormComponent/PrecognitionBeforeValidation.vue | 6 +++---
.../Pages/FormComponent/PrecognitionCallbacks.vue | 7 -------
.../Pages/FormComponent/PrecognitionDefaults.vue | 5 +++--
.../Pages/FormComponent/PrecognitionFiles.vue | 4 ++--
.../Pages/FormComponent/PrecognitionMethods.vue | 4 ++--
packages/vue3/test-app/package.json | 2 +-
26 files changed, 45 insertions(+), 71 deletions(-)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 566c5c9d3..2ba3e033c 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -588,7 +588,7 @@ export type FormComponentMethods = {
valid: (field: string) => boolean
invalid: (field: string) => boolean
validate: Validator['validate']
- touch: (field: string | string[]) => void
+ touch: (...fields: string[]) => void
touched(field?: string): boolean
}
diff --git a/packages/react/package.json b/packages/react/package.json
index 00caf4b73..1cabd99f6 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -70,4 +70,4 @@
"laravel-precognition": "^0.7.3",
"lodash-es": "^4.17.21"
}
-}
\ No newline at end of file
+}
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index de4d8f197..c9137d83a 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -259,9 +259,7 @@ const Form = forwardRef(
}
const touch = (...fields: string[]) => {
- if (validator) {
- validator.touch(fields)
- }
+ validator!.touch(fields)
}
const isTouched = (field?: string): boolean => {
@@ -294,7 +292,7 @@ const Form = forwardRef(
valid: (field: string) => valid.includes(field),
invalid: (field: string) => form.errors[field] !== undefined,
validate: (input?: string | NamedInputEvent | ValidationConfig, value?: unknown, config?: ValidationConfig) =>
- validator?.validate(input, value, config),
+ validator!.validate(input, value, config),
touch,
touched: isTouched,
})
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
index bbb2bd98d..cdbf63e35 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
@@ -3,8 +3,8 @@ import { isEqual } from 'lodash-es'
export default function PrecognitionBefore() {
const handleBeforeValidation = (
- newRequest: { data: Record; touched: string[] },
- oldRequest: { data: Record; touched: string[] },
+ newRequest: { data: Record; touched: string[] },
+ oldRequest: { data: Record; touched: string[] },
) => {
const payloadIsCorrect =
isEqual(newRequest, { data: { name: 'block' }, touched: ['name'] }) &&
@@ -29,11 +29,11 @@ export default function PrecognitionBefore() {
{
+ onChange={() =>
validate('name', {
- onBefore: handleBeforeValidation,
+ onBeforeValidation: handleBeforeValidation,
})
- }}
+ }
/>
{invalid('name') && {errors.name}
}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
index 370c2eb81..3b587b698 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
@@ -5,13 +5,6 @@ export default () => {
const [successCalled, setSuccessCalled] = useState(false)
const [errorCalled, setErrorCalled] = useState(false)
const [finishCalled, setFinishCalled] = useState(false)
- const [exceptionCaught, setExceptionCaught] = useState(false)
- const [exceptionMessage, setExceptionMessage] = useState('')
-
- const handleException = (error: Error) => {
- setExceptionCaught(true)
- setExceptionMessage(error.message || 'Unknown error')
- }
return (
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
index 0333b43cb..2eb4f768a 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
@@ -1,8 +1,9 @@
+import { FormComponentRef } from '@inertiajs/core'
import { Form } from '@inertiajs/react'
import { useRef } from 'react'
export default function PrecognitionDefaults() {
- const formRef = useRef
(null)
+ const formRef = useRef(null)
const handleSetDefaults = () => {
formRef.current?.defaults()
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
index 2265275c8..c438494e8 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionFiles.tsx
@@ -14,7 +14,7 @@ export default () => {
validateTimeout={100}
validateFiles={validateFilesEnabled}
>
- {({ invalid, errors, validate, valid, validating, touch }) => (
+ {({ invalid, errors, validate, valid, validating }) => (
<>
validate('name')} />
@@ -34,7 +34,7 @@ export default () => {
Toggle Validate Files ({validateFilesEnabled ? 'enabled' : 'disabled'})
-
validate(['name', 'avatar'])}>
+ validate({ only: ['name', 'avatar'] })}>
Validate Both
>
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
index 3fb5fdca3..b29f5d8d4 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionMethods.tsx
@@ -30,10 +30,10 @@ export default () => {
validate('name')}>
Validate Name
- validate(['name', 'email'])}>
+ validate({ only: ['name', 'email'] })}>
Validate Name and Email
- touch(['name', 'email'])}>
+ touch('name', 'email')}>
Touch Name and Email
{#if Array.isArray(errors.name)}
- {#each errors.name as error, index}
+ {#each errors.name as error, index (index)}
{error}
{/each}
{:else}
@@ -39,7 +39,7 @@
{#if invalid('email')}
{#if Array.isArray(errors.email)}
- {#each errors.email as error, index}
+ {#each errors.email as error, index (index)}
{error}
{/each}
{:else}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
index 337b9dae8..fd0ff5751 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
@@ -3,8 +3,8 @@
import { Form } from '@inertiajs/svelte'
const handleBeforeValidation = (
- newRequest: { data: Record
; touched: string[] },
- oldRequest: { data: Record; touched: string[] },
+ newRequest: { data: Record; touched: string[] },
+ oldRequest: { data: Record; touched: string[] },
) => {
const payloadIsCorrect =
isEqual(newRequest, { data: { name: 'block' }, touched: ['name'] }) &&
@@ -38,7 +38,7 @@
name="name"
on:change={() =>
validate('name', {
- onBefore: handleBeforeValidation,
+ onBeforeValidation: handleBeforeValidation,
})}
/>
{#if invalid('name')}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
index af497791a..ea23cad79 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
@@ -4,13 +4,6 @@
let successCalled = false
let errorCalled = false
let finishCalled = false
- let exceptionCaught = false
- let exceptionMessage = ''
-
- const handleException = (error: Error) => {
- exceptionCaught = true
- exceptionMessage = error.message || 'Unknown error'
- }
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
index 31475a453..01dc4b324 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
@@ -1,7 +1,8 @@
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
index 4341388a4..d49acd90c 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
@@ -1,11 +1,12 @@
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
index cb2a7e14b..8e55b5374 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionFiles.vue
@@ -14,7 +14,7 @@ const validateFilesEnabled = ref(false)
method="post"
:validate-timeout="100"
:validate-files="validateFilesEnabled"
- #default="{ invalid, errors, validate, valid, validating, touch }"
+ #default="{ invalid, errors, validate, valid, validating }"
>
@@ -38,7 +38,7 @@ const validateFilesEnabled = ref(false)
Toggle Validate Files ({{ validateFilesEnabled ? 'enabled' : 'disabled' }})
- Validate Both
+ Validate Both
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
index af27c4991..aa1ba87ba 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionMethods.vue
@@ -34,8 +34,8 @@ import { Form } from '@inertiajs/vue3'
Validate All Touched
Validate Name
-
Validate Name and Email
-
Touch Name and Email
+
Validate Name and Email
+
Touch Name and Email
Date: Thu, 30 Oct 2025 15:27:33 +0000
Subject: [PATCH 061/145] Fix code style
---
packages/core/package.json | 2 +-
playgrounds/react/package.json | 2 +-
playgrounds/svelte4/package.json | 2 +-
playgrounds/svelte5/package.json | 2 +-
playgrounds/vue3/package.json | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/core/package.json b/packages/core/package.json
index 9e8b569e0..1679fe44e 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -69,4 +69,4 @@
"laravel-precognition": "^0.7.3",
"typescript": "^5.9.3"
}
-}
\ No newline at end of file
+}
diff --git a/playgrounds/react/package.json b/playgrounds/react/package.json
index 855b5fdc3..ffa86dc14 100644
--- a/playgrounds/react/package.json
+++ b/playgrounds/react/package.json
@@ -26,4 +26,4 @@
"typescript": "^4.9.5",
"vite": "^6.4.1"
}
-}
\ No newline at end of file
+}
diff --git a/playgrounds/svelte4/package.json b/playgrounds/svelte4/package.json
index 02ed044e1..a3c0f51d2 100644
--- a/playgrounds/svelte4/package.json
+++ b/playgrounds/svelte4/package.json
@@ -23,4 +23,4 @@
"typescript": "^5.9.3",
"vite": "^5.4.21"
}
-}
\ No newline at end of file
+}
diff --git a/playgrounds/svelte5/package.json b/playgrounds/svelte5/package.json
index 77df390db..f1eaf9f8f 100644
--- a/playgrounds/svelte5/package.json
+++ b/playgrounds/svelte5/package.json
@@ -23,4 +23,4 @@
"typescript": "^5.9.3",
"vite": "^6.4.1"
}
-}
\ No newline at end of file
+}
diff --git a/playgrounds/vue3/package.json b/playgrounds/vue3/package.json
index dc2db54a6..611843520 100644
--- a/playgrounds/vue3/package.json
+++ b/playgrounds/vue3/package.json
@@ -25,4 +25,4 @@
"vue-tsc": "^2.2.12"
},
"type": "module"
-}
\ No newline at end of file
+}
From adb1a47cd6c49792e14cb4961f432f6e73188b4b Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 16:36:09 +0100
Subject: [PATCH 062/145] Revert custom implementations
---
packages/core/src/debounce.ts | 10 +++++---
packages/core/src/files.ts | 44 +++--------------------------------
packages/core/src/index.ts | 1 -
3 files changed, 10 insertions(+), 45 deletions(-)
diff --git a/packages/core/src/debounce.ts b/packages/core/src/debounce.ts
index dc0c90e1f..1755318a4 100644
--- a/packages/core/src/debounce.ts
+++ b/packages/core/src/debounce.ts
@@ -1,3 +1,7 @@
-import { debounce } from 'lodash-es'
-
-export default debounce
+export default function debounce ReturnType>(fn: F, delay: number): F {
+ let timeoutID: NodeJS.Timeout
+ return function (...args: unknown[]) {
+ clearTimeout(timeoutID)
+ timeoutID = setTimeout(() => fn.apply(this, args), delay)
+ } as F
+}
diff --git a/packages/core/src/files.ts b/packages/core/src/files.ts
index 34d271831..4d927f7b2 100644
--- a/packages/core/src/files.ts
+++ b/packages/core/src/files.ts
@@ -1,49 +1,11 @@
import { FormDataConvertible, RequestPayload } from './types'
-export function isFile(value: unknown): boolean {
- return (
- (typeof File !== 'undefined' && value instanceof File) ||
- value instanceof Blob ||
- (typeof FileList !== 'undefined' && value instanceof FileList && value.length > 0)
- )
-}
-
export function hasFiles(data: RequestPayload | FormDataConvertible): boolean {
return (
- isFile(data) ||
+ data instanceof File ||
+ data instanceof Blob ||
+ (data instanceof FileList && data.length > 0) ||
(data instanceof FormData && Array.from(data.values()).some((value) => hasFiles(value))) ||
(typeof data === 'object' && data !== null && Object.values(data).some((value) => hasFiles(value)))
)
}
-
-export function forgetFiles(data: Record): Record {
- const newData = { ...data }
-
- Object.keys(newData).forEach((name) => {
- const value = newData[name]
-
- if (value === null) {
- return
- }
-
- if (isFile(value)) {
- delete newData[name]
-
- return
- }
-
- if (Array.isArray(value)) {
- newData[name] = Object.values(forgetFiles({ ...value }))
-
- return
- }
-
- if (typeof value === 'object') {
- newData[name] = forgetFiles(newData[name] as Record)
-
- return
- }
- })
-
- return newData
-}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 16bea3267..0a9c8eceb 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -3,7 +3,6 @@ import { Router } from './router'
export { config } from './config'
export { getScrollableParent } from './domUtils'
-export { hasFiles } from './files'
export { objectToFormData } from './formData'
export { formDataToObject } from './formObject'
export { default as createHeadManager } from './head'
From 2d2307aba1bdfa04daa8c89302d4f0cb87a4501d Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 16:49:28 +0100
Subject: [PATCH 063/145] Remove dep
---
packages/core/package.json | 1 -
packages/core/src/types.ts | 30 +++++++++++++++++++++++++++---
pnpm-lock.yaml | 3 ---
3 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/packages/core/package.json b/packages/core/package.json
index 1679fe44e..a3c44da44 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -66,7 +66,6 @@
"es-check": "^9.4.4",
"esbuild": "^0.25.11",
"esbuild-node-externals": "^1.18.0",
- "laravel-precognition": "^0.7.3",
"typescript": "^5.9.3"
}
}
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 2ba3e033c..c5429e212 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -1,5 +1,4 @@
-import { AxiosProgressEvent, AxiosResponse } from 'axios'
-import { Validator } from 'laravel-precognition'
+import { AxiosError, AxiosProgressEvent, AxiosRequestConfig, AxiosResponse } from 'axios'
import { Response } from './response'
declare module 'axios' {
@@ -575,6 +574,31 @@ export type FormComponentProps = Partial<
simpleValidationErrors?: boolean
}
+interface RevalidatePayload {
+ data: Record | null
+ touched: Array
+}
+
+export type AxiosStatusHandler = (response: AxiosResponse, axiosError?: AxiosError) => unknown
+
+type PrecognitionValidationConfig = AxiosRequestConfig & {
+ precognitive?: boolean
+ only?: Iterable | ArrayLike
+ fingerprint?: string | null
+ onBefore?: () => boolean | undefined
+ onStart?: () => void
+ onSuccess?: (response: AxiosResponse) => unknown
+ onPrecognitionSuccess?: (response: AxiosResponse) => unknown
+ onValidationError?: AxiosStatusHandler
+ onUnauthorized?: AxiosStatusHandler
+ onForbidden?: AxiosStatusHandler
+ onNotFound?: AxiosStatusHandler
+ onConflict?: AxiosStatusHandler
+ onLocked?: AxiosStatusHandler
+ onFinish?: () => void
+ onBeforeValidation?: (newRequest: RevalidatePayload, oldRequest: RevalidatePayload) => boolean | undefined
+}
+
export type FormComponentMethods = {
clearErrors: (...fields: string[]) => void
resetAndClearErrors: (...fields: string[]) => void
@@ -587,7 +611,7 @@ export type FormComponentMethods = {
getFormData: () => FormData
valid: (field: string) => boolean
invalid: (field: string) => boolean
- validate: Validator['validate']
+ validate(input?: string | PrecognitionValidationConfig, value?: unknown, config?: PrecognitionValidationConfig): void
touch: (...fields: string[]) => void
touched(field?: string): boolean
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ded5a93d2..411d7557a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -64,9 +64,6 @@ importers:
esbuild-node-externals:
specifier: ^1.18.0
version: 1.18.0(esbuild@0.25.11)
- laravel-precognition:
- specifier: ^0.7.3
- version: 0.7.3
typescript:
specifier: ^5.9.3
version: 5.9.3
From 7a334eab71e27e6b6e4070a5e7a70531cb87b94c Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 16:51:08 +0100
Subject: [PATCH 064/145] Revert "Remove dep"
This reverts commit 2d2307aba1bdfa04daa8c89302d4f0cb87a4501d.
---
packages/core/package.json | 1 +
packages/core/src/types.ts | 30 +++---------------------------
pnpm-lock.yaml | 3 +++
3 files changed, 7 insertions(+), 27 deletions(-)
diff --git a/packages/core/package.json b/packages/core/package.json
index a3c44da44..1679fe44e 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -66,6 +66,7 @@
"es-check": "^9.4.4",
"esbuild": "^0.25.11",
"esbuild-node-externals": "^1.18.0",
+ "laravel-precognition": "^0.7.3",
"typescript": "^5.9.3"
}
}
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index c5429e212..2ba3e033c 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -1,4 +1,5 @@
-import { AxiosError, AxiosProgressEvent, AxiosRequestConfig, AxiosResponse } from 'axios'
+import { AxiosProgressEvent, AxiosResponse } from 'axios'
+import { Validator } from 'laravel-precognition'
import { Response } from './response'
declare module 'axios' {
@@ -574,31 +575,6 @@ export type FormComponentProps = Partial<
simpleValidationErrors?: boolean
}
-interface RevalidatePayload {
- data: Record | null
- touched: Array
-}
-
-export type AxiosStatusHandler = (response: AxiosResponse, axiosError?: AxiosError) => unknown
-
-type PrecognitionValidationConfig = AxiosRequestConfig & {
- precognitive?: boolean
- only?: Iterable | ArrayLike
- fingerprint?: string | null
- onBefore?: () => boolean | undefined
- onStart?: () => void
- onSuccess?: (response: AxiosResponse) => unknown
- onPrecognitionSuccess?: (response: AxiosResponse) => unknown
- onValidationError?: AxiosStatusHandler
- onUnauthorized?: AxiosStatusHandler
- onForbidden?: AxiosStatusHandler
- onNotFound?: AxiosStatusHandler
- onConflict?: AxiosStatusHandler
- onLocked?: AxiosStatusHandler
- onFinish?: () => void
- onBeforeValidation?: (newRequest: RevalidatePayload, oldRequest: RevalidatePayload) => boolean | undefined
-}
-
export type FormComponentMethods = {
clearErrors: (...fields: string[]) => void
resetAndClearErrors: (...fields: string[]) => void
@@ -611,7 +587,7 @@ export type FormComponentMethods = {
getFormData: () => FormData
valid: (field: string) => boolean
invalid: (field: string) => boolean
- validate(input?: string | PrecognitionValidationConfig, value?: unknown, config?: PrecognitionValidationConfig): void
+ validate: Validator['validate']
touch: (...fields: string[]) => void
touched(field?: string): boolean
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 411d7557a..ded5a93d2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -64,6 +64,9 @@ importers:
esbuild-node-externals:
specifier: ^1.18.0
version: 1.18.0(esbuild@0.25.11)
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
typescript:
specifier: ^5.9.3
version: 5.9.3
From 1691cc771cc9c16370ae88ff74e1657a6b13d457 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 16:58:55 +0100
Subject: [PATCH 065/145] align
---
packages/core/package.json | 2 +-
packages/core/src/types.ts | 1 +
packages/react/src/Form.ts | 27 ++++++------
packages/svelte/src/components/Form.svelte | 19 ++++-----
packages/vue3/src/form.ts | 17 ++++----
playgrounds/svelte5/package.json | 2 +-
pnpm-lock.yaml | 48 +++++++++++-----------
7 files changed, 56 insertions(+), 60 deletions(-)
diff --git a/packages/core/package.json b/packages/core/package.json
index 1679fe44e..2ca3c2ea3 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -55,6 +55,7 @@
"dependencies": {
"@types/lodash-es": "^4.17.12",
"axios": "^1.13.1",
+ "laravel-precognition": "^0.7.3",
"lodash-es": "^4.17.21",
"qs": "^6.14.0"
},
@@ -66,7 +67,6 @@
"es-check": "^9.4.4",
"esbuild": "^0.25.11",
"esbuild-node-externals": "^1.18.0",
- "laravel-precognition": "^0.7.3",
"typescript": "^5.9.3"
}
}
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 2ba3e033c..9223ac625 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -602,6 +602,7 @@ export type FormComponentState = {
wasSuccessful: boolean
recentlySuccessful: boolean
isDirty: boolean
+ validator: Validator
validating: boolean
}
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index c9137d83a..6a57b4a74 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -94,7 +94,7 @@ const Form = forwardRef(
const [valid, setValid] = useState([])
const [touched, setTouched] = useState([])
- const [validator, setValidator] = useState(null)
+ const [validator, setValidator] = useState()
const getFormData = (): FormData => new FormData(formElement.current)
@@ -120,12 +120,10 @@ const Form = forwardRef(
const clearErrors = (...names: string[]) => {
form.clearErrors(...names)
- if (validator) {
- if (names.length === 0) {
- validator.setErrors({})
- } else {
- names.forEach(validator.forgetError)
- }
+ if (names.length === 0) {
+ validator!.setErrors({})
+ } else {
+ names.forEach(validator!.forgetError)
}
return form
@@ -163,11 +161,11 @@ const Form = forwardRef(
.on('errorsChanged', () => {
form.clearErrors()
- form.setError(
- (simpleValidationErrors
- ? toSimpleValidationErrors(newValidator.errors())
- : newValidator.errors()) as Errors,
- )
+ const errors = simpleValidationErrors
+ ? toSimpleValidationErrors(newValidator.errors())
+ : newValidator.errors()
+
+ form.setError(errors as Errors)
setValid(newValidator.valid())
})
@@ -186,9 +184,7 @@ const Form = forwardRef(
}, [])
useEffect(() => {
- if (validator) {
- validator.setTimeout(validateTimeout)
- }
+ validator!.setTimeout(validateTimeout)
}, [validateTimeout, validator])
const reset = (...fields: string[]) => {
@@ -288,6 +284,7 @@ const Form = forwardRef(
getFormData,
// Precognition
+ validator: validator!,
validating,
valid: (field: string) => valid.includes(field),
invalid: (field: string) => form.errors[field] !== undefined,
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index 59cb6ef23..ee5bf7f37 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -59,7 +59,7 @@
let validating = false
let validFields: string[] = []
let touchedFields: string[] = []
- let validator: Validator | null = null
+ let validator: Validator
$: _method = isUrlMethodPair(action) ? action.method : ((method ?? 'get').toLowerCase() as Method)
$: _action = isUrlMethodPair(action) ? action.url : (action as string)
@@ -168,12 +168,10 @@
// @ts-expect-error
$form.clearErrors(...fields)
- if (validator) {
- if (fields.length === 0) {
- validator.setErrors({})
- } else {
- fields.forEach(validator.forgetError)
- }
+ if (fields.length === 0) {
+ validator!.setErrors({})
+ } else {
+ fields.forEach(validator!.forgetError)
}
}
@@ -249,10 +247,8 @@
touchedFields = validator!.touched()
})
.on('errorsChanged', () => {
- // Clear form errors first
$form.clearErrors()
- // Set new errors
const errors = simpleValidationErrors ? toSimpleValidationErrors(validator!.errors()) : validator!.errors()
$form.setError(errors as Errors)
@@ -271,9 +267,7 @@
})
$: {
- if (validator) {
- validator.setTimeout(validateTimeout!)
- }
+ validator!.setTimeout(validateTimeout!)
}
$: slotErrors = $form.errors as Errors
@@ -310,6 +304,7 @@
{reset}
{getData}
{getFormData}
+ {validator}
{validating}
{validate}
{touch}
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 5cbed2062..27a0c7954 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -198,11 +198,11 @@ const Form = defineComponent({
.on('errorsChanged', () => {
form.clearErrors()
- form.setError(
- (props.simpleValidationErrors
- ? toSimpleValidationErrors(validator.errors())
- : validator.errors()) as Errors,
- )
+ const errors = simpleValidationErrors
+ ? toSimpleValidationErrors(newValidator.errors())
+ : newValidator.errors()
+
+ form.setError(errors as Errors)
valid.value = validator.valid()
})
@@ -334,6 +334,9 @@ const Form = defineComponent({
get recentlySuccessful() {
return form.recentlySuccessful
},
+ get validator() {
+ return validator
+ },
get validating() {
return validating.value
},
@@ -354,8 +357,8 @@ const Form = defineComponent({
valid: (field: string) => valid.value.includes(field),
invalid: (field: string) => form.errors[field] !== undefined,
validate: (input?: string | NamedInputEvent | ValidationConfig, value?: unknown, config?: ValidationConfig) =>
- validator!.validate(input, value, config),
- touch: (...fields: string[]) => validator!.touch(fields),
+ validator.validate(input, value, config),
+ touch: (...fields: string[]) => validator.touch(fields),
touched: isTouched,
}
diff --git a/playgrounds/svelte5/package.json b/playgrounds/svelte5/package.json
index f1eaf9f8f..a45794054 100644
--- a/playgrounds/svelte5/package.json
+++ b/playgrounds/svelte5/package.json
@@ -17,7 +17,7 @@
"axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
- "svelte": "^5.43.1",
+ "svelte": "^5.43.2",
"svelte-check": "^4.3.3",
"tailwindcss": "^4.1.16",
"typescript": "^5.9.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ded5a93d2..96ccd6cbf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,10 +19,10 @@ importers:
version: 4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3))
prettier-plugin-svelte:
specifier: ^3.4.0
- version: 3.4.0(prettier@3.6.2)(svelte@5.43.1)
+ version: 3.4.0(prettier@3.6.2)(svelte@5.43.2)
prettier-plugin-tailwindcss:
specifier: ^0.6.14
- version: 0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.1))(prettier@3.6.2)
+ version: 0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2))(prettier@3.6.2)
optionalDependencies:
'@rollup/rollup-linux-x64-gnu':
specifier: ^4.52.5
@@ -36,6 +36,9 @@ importers:
axios:
specifier: ^1.13.1
version: 1.13.1
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
@@ -64,9 +67,6 @@ importers:
esbuild-node-externals:
specifier: ^1.18.0
version: 1.18.0(esbuild@0.25.11)
- laravel-precognition:
- specifier: ^0.7.3
- version: 0.7.3
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -465,7 +465,7 @@ importers:
version: link:../../packages/svelte
'@sveltejs/vite-plugin-svelte':
specifier: ^5.1.1
- version: 5.1.1(svelte@5.43.1)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 5.1.1(svelte@5.43.2)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@tailwindcss/vite':
specifier: ^4.1.16
version: 4.1.16(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
@@ -482,11 +482,11 @@ importers:
specifier: ^4.17.21
version: 4.17.21
svelte:
- specifier: ^5.43.1
- version: 5.43.1
+ specifier: ^5.43.2
+ version: 5.43.2
svelte-check:
specifier: ^4.3.3
- version: 4.3.3(picomatch@4.0.3)(svelte@5.43.1)(typescript@5.9.3)
+ version: 4.3.3(picomatch@4.0.3)(svelte@5.43.2)(typescript@5.9.3)
tailwindcss:
specifier: ^4.1.16
version: 4.1.16
@@ -3666,8 +3666,8 @@ packages:
resolution: {integrity: sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==}
engines: {node: '>=16'}
- svelte@5.43.1:
- resolution: {integrity: sha512-HwXMvQbSFZD5AqmjXzc1bJ1qPFM+iMyUwttmZjtCruIPLz7tG3RYFfzICotaf9HaR5qszzzTRe2rE/ps4mxGLg==}
+ svelte@5.43.2:
+ resolution: {integrity: sha512-ro1umEzX8rT5JpCmlf0PPv7ncD8MdVob9e18bhwqTKNoLjS8kDvhVpaoYVPc+qMwDAOfcwJtyY7ZFSDbOaNPgA==}
engines: {node: '>=18'}
table@6.9.0:
@@ -4587,11 +4587,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.1)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.1)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.2)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.2)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.43.1)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.43.2)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
debug: 4.4.3(supports-color@5.5.0)
- svelte: 5.43.1
+ svelte: 5.43.2
vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
transitivePeerDependencies:
- supports-color
@@ -4610,14 +4610,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.1)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.2)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.1)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.1)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.2)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.2)(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
debug: 4.4.3(supports-color@5.5.0)
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.21
- svelte: 5.43.1
+ svelte: 5.43.2
vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vitefu: 1.1.1(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
transitivePeerDependencies:
@@ -6856,17 +6856,17 @@ snapshots:
optionalDependencies:
vue-tsc: 2.2.12(typescript@5.9.3)
- prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.1):
+ prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2):
dependencies:
prettier: 3.6.2
- svelte: 5.43.1
+ svelte: 5.43.2
- prettier-plugin-tailwindcss@0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.1))(prettier@3.6.2):
+ prettier-plugin-tailwindcss@0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2))(prettier@3.6.2):
dependencies:
prettier: 3.6.2
optionalDependencies:
prettier-plugin-organize-imports: 4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3))
- prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.43.1)
+ prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.43.2)
prettier@3.6.2: {}
@@ -7280,14 +7280,14 @@ snapshots:
transitivePeerDependencies:
- picomatch
- svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.43.1)(typescript@5.9.3):
+ svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.43.2)(typescript@5.9.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
chokidar: 4.0.3
fdir: 6.5.0(picomatch@4.0.3)
picocolors: 1.1.1
sade: 1.8.1
- svelte: 5.43.1
+ svelte: 5.43.2
typescript: 5.9.3
transitivePeerDependencies:
- picomatch
@@ -7331,7 +7331,7 @@ snapshots:
magic-string: 0.30.21
periscopic: 3.1.0
- svelte@5.43.1:
+ svelte@5.43.2:
dependencies:
'@jridgewell/remapping': 2.3.5
'@jridgewell/sourcemap-codec': 1.5.5
From 66aa717c15a28a7ad49d779e25d577b9de845c13 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 17:01:24 +0100
Subject: [PATCH 066/145] Update form.ts
---
packages/vue3/src/form.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 27a0c7954..467aa02bc 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -198,9 +198,9 @@ const Form = defineComponent({
.on('errorsChanged', () => {
form.clearErrors()
- const errors = simpleValidationErrors
- ? toSimpleValidationErrors(newValidator.errors())
- : newValidator.errors()
+ const errors = props.simpleValidationErrors
+ ? toSimpleValidationErrors(validator.errors())
+ : validator.errors()
form.setError(errors as Errors)
From 1040f92f8351124459d2d2b5f698c45efcfbf01f Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Thu, 30 Oct 2025 18:16:54 +0100
Subject: [PATCH 067/145] fix watchers
---
packages/react/src/Form.ts | 2 +-
packages/svelte/src/components/Form.svelte | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index 6a57b4a74..c6ead7e52 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -184,7 +184,7 @@ const Form = forwardRef(
}, [])
useEffect(() => {
- validator!.setTimeout(validateTimeout)
+ validator?.setTimeout(validateTimeout)
}, [validateTimeout, validator])
const reset = (...fields: string[]) => {
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index ee5bf7f37..d76914b05 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -267,7 +267,7 @@
})
$: {
- validator!.setTimeout(validateTimeout!)
+ validator?.setTimeout(validateTimeout!)
}
$: slotErrors = $form.errors as Errors
From 63a2ad9892b51a0a6dbd5984650323091e180266 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 13:16:46 +0100
Subject: [PATCH 068/145] wip
---
packages/core/src/types.ts | 4 ++--
packages/react/src/Form.ts | 21 ++++++++++++++++++---
packages/svelte/src/components/Form.svelte | 22 +++++++++++++++-------
packages/vue3/src/form.ts | 21 ++++++++++++++++++---
tests/app/server.js | 14 ++++----------
tests/form-component.spec.ts | 13 +++++++++++++
6 files changed, 70 insertions(+), 25 deletions(-)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 9223ac625..563743153 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -1,5 +1,5 @@
import { AxiosProgressEvent, AxiosResponse } from 'axios'
-import { Validator } from 'laravel-precognition'
+import { NamedInputEvent, ValidationConfig, Validator } from 'laravel-precognition'
import { Response } from './response'
declare module 'axios' {
@@ -587,7 +587,7 @@ export type FormComponentMethods = {
getFormData: () => FormData
valid: (field: string) => boolean
invalid: (field: string) => boolean
- validate: Validator['validate']
+ validate(field?: string | NamedInputEvent | ValidationConfig, config?: ValidationConfig): void
touch: (...fields: string[]) => void
touched(field?: string): boolean
}
diff --git a/packages/react/src/Form.ts b/packages/react/src/Form.ts
index c6ead7e52..1c8da2b2f 100644
--- a/packages/react/src/Form.ts
+++ b/packages/react/src/Form.ts
@@ -14,11 +14,12 @@ import {
import {
createValidator,
NamedInputEvent,
+ resolveName,
toSimpleValidationErrors,
ValidationConfig,
Validator,
} from 'laravel-precognition'
-import { isEqual } from 'lodash-es'
+import { get, isEqual } from 'lodash-es'
import React, {
createElement,
FormEvent,
@@ -183,6 +184,21 @@ const Form = forwardRef(
}
}, [])
+ const validate = (field?: string | NamedInputEvent | ValidationConfig, config?: ValidationConfig) => {
+ if (typeof field === 'object' && !('target' in field)) {
+ config = field
+ field = undefined
+ }
+
+ if (typeof field === 'undefined') {
+ validator!.validate(config)
+ } else {
+ field = resolveName(field)
+
+ validator!.validate(field, get(getTransformedData(), field), config)
+ }
+ }
+
useEffect(() => {
validator?.setTimeout(validateTimeout)
}, [validateTimeout, validator])
@@ -288,8 +304,7 @@ const Form = forwardRef(
validating,
valid: (field: string) => valid.includes(field),
invalid: (field: string) => form.errors[field] !== undefined,
- validate: (input?: string | NamedInputEvent | ValidationConfig, value?: unknown, config?: ValidationConfig) =>
- validator!.validate(input, value, config),
+ validate,
touch,
touched: isTouched,
})
diff --git a/packages/svelte/src/components/Form.svelte b/packages/svelte/src/components/Form.svelte
index d76914b05..5fefd45d3 100644
--- a/packages/svelte/src/components/Form.svelte
+++ b/packages/svelte/src/components/Form.svelte
@@ -12,12 +12,13 @@
} from '@inertiajs/core'
import {
createValidator,
+ resolveName,
toSimpleValidationErrors,
type NamedInputEvent,
type ValidationConfig,
type Validator,
} from 'laravel-precognition'
- import { isEqual } from 'lodash-es'
+ import { get, isEqual } from 'lodash-es'
import { onMount } from 'svelte'
import useForm from '../useForm'
@@ -194,12 +195,19 @@
isDirty = false
}
- export function validate(
- input?: string | NamedInputEvent | ValidationConfig,
- value?: unknown,
- config?: ValidationConfig,
- ) {
- return validator!.validate(input, value, config)
+ export function validate(field?: string | NamedInputEvent | ValidationConfig, config?: ValidationConfig) {
+ if (typeof field === 'object' && !('target' in field)) {
+ config = field
+ field = undefined
+ }
+
+ if (typeof field === 'undefined') {
+ validator!.validate(config)
+ } else {
+ field = resolveName(field)
+
+ validator!.validate(field, get(getTransformedData(), field), config)
+ }
}
export function touch(...fields: string[]) {
diff --git a/packages/vue3/src/form.ts b/packages/vue3/src/form.ts
index 467aa02bc..789950534 100644
--- a/packages/vue3/src/form.ts
+++ b/packages/vue3/src/form.ts
@@ -14,11 +14,12 @@ import {
import {
createValidator,
NamedInputEvent,
+ resolveName,
toSimpleValidationErrors,
ValidationConfig,
Validator,
} from 'laravel-precognition'
-import { isEqual } from 'lodash-es'
+import { get, isEqual } from 'lodash-es'
import { computed, defineComponent, h, onBeforeUnmount, onMounted, PropType, ref, SlotsType, watch } from 'vue'
import useForm from './useForm'
@@ -217,6 +218,21 @@ const Form = defineComponent({
formEvents.forEach((e) => formElement.value.addEventListener(e, onFormUpdate))
})
+ const validate = (field?: string | NamedInputEvent | ValidationConfig, config?: ValidationConfig) => {
+ if (typeof field === 'object' && !('target' in field)) {
+ config = field
+ field = undefined
+ }
+
+ if (typeof field === 'undefined') {
+ validator.validate(config)
+ } else {
+ field = resolveName(field)
+
+ validator.validate(field, get(getTransformedData(), field), config)
+ }
+ }
+
// watch(
// () => props.validateFiles,
// (value) => validator.validateFiles(value),
@@ -356,8 +372,7 @@ const Form = defineComponent({
// Precognition
valid: (field: string) => valid.value.includes(field),
invalid: (field: string) => form.errors[field] !== undefined,
- validate: (input?: string | NamedInputEvent | ValidationConfig, value?: unknown, config?: ValidationConfig) =>
- validator.validate(input, value, config),
+ validate,
touch: (...fields: string[]) => validator.touch(fields),
touched: isTouched,
}
diff --git a/tests/app/server.js b/tests/app/server.js
index b4d064d6a..757084e44 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -996,7 +996,7 @@ app.post('/form-component/precognition', (req, res) => {
return res.status(204).header('Precognition-Success', 'true').send()
},
- !!req.query['slow'] ? 2000 : 500,
+ !!req.query['slow'] ? 2000 : 250,
)
})
@@ -1039,7 +1039,7 @@ app.post('/form-component/precognition-array-errors', (req, res) => {
}
return res.status(204).header('Precognition-Success', 'true').send()
- }, 500)
+ }, 250)
})
app.post('/form-component/precognition-files', upload.any(), (req, res) => {
@@ -1078,7 +1078,7 @@ app.post('/form-component/precognition-files', upload.any(), (req, res) => {
}
return res.status(204).header('Precognition-Success', 'true').send()
- }, 500)
+ }, 250)
})
app.post('/form-component/precognition-headers', (req, res) => {
@@ -1104,13 +1104,7 @@ app.post('/form-component/precognition-headers', (req, res) => {
}
return res.status(204).header('Precognition-Success', 'true').send()
- }, 500)
-})
-
-app.post('/form-component/precognition-exception', (req, res) => {
- setTimeout(() => {
- res.status(500).json({ message: 'Internal server error' })
- }, 500)
+ }, 250)
})
//
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 8acf943e9..4d37ecab0 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1436,6 +1436,19 @@ test.describe('Form Component', () => {
await page.goto('/form-component/precognition')
})
+ test('does not validate when field is untouched', async ({ page }) => {
+ await page.locator('input[name="name"]').focus()
+ await page.waitForTimeout(100)
+ await page.locator('input[name="name"]').blur()
+
+ for (let i = 0; i < 5; i++) {
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await page.waitForTimeout(50)
+ }
+
+ await expect(page.getByText('The name field is required.')).not.toBeVisible()
+ })
+
test('shows validation error when field is invalid', async ({ page }) => {
await page.fill('input[name="name"]', 'ab')
await page.locator('input[name="name"]').blur()
From 8323d9966b61e154976c60b8010de6d8d7bfb121 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 13:17:51 +0100
Subject: [PATCH 069/145] Update types.ts
---
packages/core/src/types.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 563743153..c111cd5c8 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -111,8 +111,7 @@ export type FormDataError = Partial, ErrorValue>>
export type Method = 'get' | 'post' | 'put' | 'patch' | 'delete'
-export type RequestData = Record
-export type RequestPayload = RequestData | FormData
+export type RequestPayload = Record | FormData
export interface PageProps {
[key: string]: unknown
From cdad30f8e60dd4c93c3f59226a49b0ddec4f8c59 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 13:20:15 +0100
Subject: [PATCH 070/145] fix type
---
.../Pages/FormComponent/PrecognitionBeforeValidation.tsx | 6 +++---
.../Pages/FormComponent/PrecognitionBeforeValidation.svelte | 6 +++---
.../Pages/FormComponent/PrecognitionBeforeValidation.vue | 6 +++---
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
index cdbf63e35..3e8149d55 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionBeforeValidation.tsx
@@ -3,14 +3,14 @@ import { isEqual } from 'lodash-es'
export default function PrecognitionBefore() {
const handleBeforeValidation = (
- newRequest: { data: Record; touched: string[] },
- oldRequest: { data: Record; touched: string[] },
+ newRequest: { data: Record | null; touched: string[] },
+ oldRequest: { data: Record | null; touched: string[] },
) => {
const payloadIsCorrect =
isEqual(newRequest, { data: { name: 'block' }, touched: ['name'] }) &&
isEqual(oldRequest, { data: {}, touched: [] })
- if (payloadIsCorrect && newRequest.data.name === 'block') {
+ if (payloadIsCorrect && newRequest.data?.name === 'block') {
return false
}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
index fd0ff5751..82b01f9b5 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionBeforeValidation.svelte
@@ -3,15 +3,15 @@
import { Form } from '@inertiajs/svelte'
const handleBeforeValidation = (
- newRequest: { data: Record; touched: string[] },
- oldRequest: { data: Record; touched: string[] },
+ newRequest: { data: Record | null; touched: string[] },
+ oldRequest: { data: Record | null; touched: string[] },
) => {
const payloadIsCorrect =
isEqual(newRequest, { data: { name: 'block' }, touched: ['name'] }) &&
isEqual(oldRequest, { data: {}, touched: [] })
// Block validation if name is "block"
- if (payloadIsCorrect && newRequest.data.name === 'block') {
+ if (payloadIsCorrect && newRequest.data?.name === 'block') {
return false
}
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
index e3593c039..3ed761748 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionBeforeValidation.vue
@@ -3,14 +3,14 @@ import { Form } from '@inertiajs/vue3'
import { isEqual } from 'lodash-es'
const handleBeforeValidation = (
- newRequest: { data: Record; touched: string[] },
- oldRequest: { data: Record; touched: string[] },
+ newRequest: { data: Record | null; touched: string[] },
+ oldRequest: { data: Record | null; touched: string[] },
) => {
const payloadIsCorrect =
isEqual(newRequest, { data: { name: 'block' }, touched: ['name'] }) &&
isEqual(oldRequest, { data: {}, touched: [] })
- if (payloadIsCorrect && newRequest.data.name === 'block') {
+ if (payloadIsCorrect && newRequest.data?.name === 'block') {
return false
}
From 7c8a30f14b99922dd327911b60fce66edbe20f4c Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 14:28:20 +0100
Subject: [PATCH 071/145] improve tests
---
packages/core/src/resetFormFields.ts | 8 ++--
.../FormComponent/PrecognitionCallbacks.tsx | 24 ++----------
.../PrecognitionCallbacks.svelte | 24 ++----------
.../FormComponent/PrecognitionCallbacks.vue | 26 ++-----------
.../js/Pages/FormComponentPrecognition.tsx | 4 +-
.../js/Pages/FormComponentPrecognition.svelte | 4 +-
.../js/Pages/FormComponentPrecognition.svelte | 4 +-
.../js/Pages/FormComponentPrecognition.vue | 4 +-
tests/form-component.spec.ts | 37 ++++++++++---------
9 files changed, 44 insertions(+), 91 deletions(-)
diff --git a/packages/core/src/resetFormFields.ts b/packages/core/src/resetFormFields.ts
index abaecc1a6..f4948506d 100644
--- a/packages/core/src/resetFormFields.ts
+++ b/packages/core/src/resetFormFields.ts
@@ -161,8 +161,10 @@ export function resetFormFields(formElement: HTMLFormElement, defaults: FormData
return
}
+ const resetEntireForm = !fieldNames || fieldNames.length === 0
+
// If no specific fields provided, reset the entire form
- if (!fieldNames || fieldNames.length === 0) {
+ if (resetEntireForm) {
// Get all field names from both defaults and form elements (including disabled ones)
const formData = new FormData(formElement)
const formElementNames = Array.from(formElement.elements)
@@ -173,7 +175,7 @@ export function resetFormFields(formElement: HTMLFormElement, defaults: FormData
let hasChanged = false
- fieldNames.forEach((fieldName) => {
+ fieldNames!.forEach((fieldName) => {
const elements = formElement.elements.namedItem(fieldName)
if (elements) {
@@ -184,7 +186,7 @@ export function resetFormFields(formElement: HTMLFormElement, defaults: FormData
})
// Dispatch reset event if any field changed (matching native form.reset() behavior)
- if (hasChanged) {
+ if (hasChanged && resetEntireForm) {
formElement.dispatchEvent(new Event('reset', { bubbles: true }))
}
}
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
index 3b587b698..47735527b 100644
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
+++ b/packages/react/test-app/Pages/FormComponent/PrecognitionCallbacks.tsx
@@ -8,7 +8,7 @@ export default () => {
return (
-
Form Precognition Callbacks & Exceptions
+ Form Precognition Callbacks
Callbacks Test
@@ -19,8 +19,8 @@ export default () => {
{validating && Validating...
}
- {successCalled && onSuccess called!
}
- {errorCalled && onError called!
}
+ {successCalled && onPrecognitionSuccess called!
}
+ {errorCalled && onValidationError called!
}
{finishCalled && onFinish called!
}
{
onPrecognitionSuccess: () => {
setSuccessCalled(true)
},
- onFinish: () => {
- setFinishCalled(true)
- },
- })
- }}
- >
- Validate with onSuccess
-
-
- {
- setSuccessCalled(false)
- setErrorCalled(false)
- setFinishCalled(false)
- validate({
onValidationError: () => {
setErrorCalled(true)
},
@@ -58,7 +42,7 @@ export default () => {
})
}}
>
- Validate with onError
+ Validate
>
)}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
index ea23cad79..1b85ddba3 100644
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
+++ b/packages/svelte/test-app/Pages/FormComponent/PrecognitionCallbacks.svelte
@@ -7,7 +7,7 @@
-
Form Precognition Callbacks & Exceptions
+
Form Precognition Callbacks
Callbacks Test
@@ -19,10 +19,10 @@
Validating...
{/if}
{#if successCalled}
- onSuccess called!
+ onPrecognitionSuccess called!
{/if}
{#if errorCalled}
- onError called!
+ onValidationError called!
{/if}
{#if finishCalled}
onFinish called!
@@ -38,22 +38,6 @@
onPrecognitionSuccess: () => {
successCalled = true
},
- onFinish: () => {
- finishCalled = true
- },
- })
- }}
- >
- Validate with onSuccess
-
-
- {
- successCalled = false
- errorCalled = false
- finishCalled = false
- validate({
onValidationError: () => {
errorCalled = true
},
@@ -63,7 +47,7 @@
})
}}
>
- Validate with onError
+ Validate
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
index ee4d996ad..824d98b4b 100644
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
+++ b/packages/vue3/test-app/Pages/FormComponent/PrecognitionCallbacks.vue
@@ -9,7 +9,7 @@ const finishCalled = ref(false)
-
Form Precognition Callbacks & Exceptions
+ Form Precognition Callbacks
Callbacks Test
@@ -19,8 +19,8 @@ const finishCalled = ref(false)
Validating...
- onSuccess called!
- onError called!
+ onPrecognitionSuccess called!
+ onValidationError called!
onFinish called!
{
successCalled = true
},
- onFinish: () => {
- finishCalled = true
- },
- })
- }
- "
- >
- Validate with onSuccess
-
-
- {
- successCalled = false
- errorCalled = false
- finishCalled = false
- validate({
onValidationError: () => {
errorCalled = true
},
@@ -62,7 +44,7 @@ const finishCalled = ref(false)
}
"
>
- Validate with onError
+ Validate
diff --git a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
index d0388b83a..f64cb7139 100644
--- a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
+++ b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
@@ -245,8 +245,8 @@ const FormComponentPrecognition = () => {
{(callbacks.success || callbacks.error || callbacks.finish) && (
- {callbacks.success &&
onSuccess called!
}
- {callbacks.error &&
onError called!
}
+ {callbacks.success &&
onPrecognitionSuccess called!
}
+ {callbacks.error &&
onValidationError called!
}
{callbacks.finish &&
onFinish called!
}
{callbacks.exception && (
onException: {callbacks.exceptionMessage}
diff --git a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
index 44b70730c..583d22296 100644
--- a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
@@ -256,10 +256,10 @@
{#if callbacks.success || callbacks.error || callbacks.finish}
{#if callbacks.success}
-
onSuccess called!
+
onPrecognitionSuccess called!
{/if}
{#if callbacks.error}
-
onError called!
+
onValidationError called!
{/if}
{#if callbacks.finish}
onFinish called!
diff --git a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
index 30b2ade76..caa39e56b 100644
--- a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
@@ -234,10 +234,10 @@
{#if callbacks.success || callbacks.error || callbacks.finish}
{#if callbacks.success}
-
onSuccess called!
+
onPrecognitionSuccess called!
{/if}
{#if callbacks.error}
-
onError called!
+
onValidationError called!
{/if}
{#if callbacks.finish}
onFinish called!
diff --git a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
index 384fc0b71..5b649ef61 100644
--- a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
+++ b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
@@ -209,8 +209,8 @@ const validateTimeout = ref(1500)
Validating...
-
onSuccess called!
-
onError called!
+
onPrecognitionSuccess called!
+
onValidationError called!
onFinish called!
onException: {{ callbacks.exceptionMessage }}
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 4d37ecab0..038e6fe2f 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1688,17 +1688,14 @@ test.describe('Form Component', () => {
test('touch with array marks multiple fields as touched', async ({ page }) => {
await page.goto('/form-component/precognition-methods')
- await page.fill('input[name="name"]', 'ab')
- await page.fill('input[name="email"]', 'x')
-
await page.getByRole('button', { name: 'Touch Name and Email' }).click()
await page.getByRole('button', { name: 'Validate All Touched' }).click()
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
- await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ await expect(page.getByText('The name field is required.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).toBeVisible()
})
test('touch deduplicates fields when called multiple times', async ({ page }) => {
@@ -1777,16 +1774,13 @@ test.describe('Form Component', () => {
test('validate with array of fields validates multiple fields', async ({ page }) => {
await page.goto('/form-component/precognition-methods')
- await page.fill('input[name="name"]', 'ab')
- await page.fill('input[name="email"]', 'x')
-
await page.getByRole('button', { name: 'Validate Name and Email' }).click()
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
- await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ await expect(page.getByText('The name field is required.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).toBeVisible()
})
test('reset with array removes multiple fields from touched', async ({ page }) => {
@@ -1808,6 +1802,13 @@ test.describe('Form Component', () => {
await page.getByRole('button', { name: 'Reset Name and Email' }).click()
+ await expect(page.locator('input[name="name"]')).toHaveValue('')
+ await expect(page.locator('input[name="email"]')).toHaveValue('')
+
+ await expect(page.getByText('Name is not touched')).toBeVisible()
+ await expect(page.getByText('Email is not touched')).toBeVisible()
+ await expect(page.getByText('Form has no touched fields')).toBeVisible()
+
await page.fill('input[name="name"]', 'abc')
await page.fill('input[name="email"]', 'test@example.com')
@@ -1879,31 +1880,31 @@ test.describe('Form Component', () => {
await expect(page.getByText('Name is valid!')).toBeVisible()
})
- test('calls onSuccess and onFinish callbacks when validation succeeds', async ({ page }) => {
+ test('calls onPrecognitionSuccess and onFinish callbacks when validation succeeds', async ({ page }) => {
await page.goto('/form-component/precognition-callbacks')
await page.fill('input[name="name"]', 'John Doe')
- await page.click('button:has-text("Validate with onSuccess")')
+ await page.click('button:has-text("Validate")')
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- await expect(page.getByText('onSuccess called!')).toBeVisible()
- await expect(page.getByText('onError called!')).not.toBeVisible()
+ await expect(page.getByText('onPrecognitionSuccess called!')).toBeVisible()
+ await expect(page.getByText('onValidationError called!')).not.toBeVisible()
await expect(page.getByText('onFinish called!')).toBeVisible()
})
- test('calls onError and onFinish callbacks when validation fails', async ({ page }) => {
+ test('calls onValidationError and onFinish callbacks when validation fails', async ({ page }) => {
await page.goto('/form-component/precognition-callbacks')
await page.fill('input[name="name"]', 'ab')
- await page.click('button:has-text("Validate with onError")')
+ await page.click('button:has-text("Validate")')
await expect(page.getByText('Validating...')).toBeVisible()
await expect(page.getByText('Validating...')).not.toBeVisible()
- await expect(page.getByText('onSuccess called!')).not.toBeVisible()
- await expect(page.getByText('onError called!')).toBeVisible()
+ await expect(page.getByText('onPrecognitionSuccess called!')).not.toBeVisible()
+ await expect(page.getByText('onValidationError called!')).toBeVisible()
await expect(page.getByText('onFinish called!')).toBeVisible()
})
From 4f2d66d6629f8b604659d0a68d5017918f8f0f8a Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 14:49:48 +0100
Subject: [PATCH 072/145] cleanup
---
.../FormComponent/PrecognitionDefaults.tsx | 38 -----------------
.../FormComponent/PrecognitionDefaults.svelte | 42 -------------------
.../FormComponent/PrecognitionDefaults.vue | 38 -----------------
tests/form-component.spec.ts | 22 ----------
4 files changed, 140 deletions(-)
delete mode 100644 packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
delete mode 100644 packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
delete mode 100644 packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
diff --git a/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx b/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
deleted file mode 100644
index 2eb4f768a..000000000
--- a/packages/react/test-app/Pages/FormComponent/PrecognitionDefaults.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { FormComponentRef } from '@inertiajs/core'
-import { Form } from '@inertiajs/react'
-import { useRef } from 'react'
-
-export default function PrecognitionDefaults() {
- const formRef = useRef(null)
-
- const handleSetDefaults = () => {
- formRef.current?.defaults()
- }
-
- return (
-
-
Precognition - Defaults Updates Validator
-
-
- {({ invalid, errors, validate, validating }) => (
- <>
-
-
- {invalid('name') &&
{errors.name}
}
-
-
- {validating && Validating...
}
-
-
- Set Defaults
-
- validate('name')}>
- Validate Name
-
- Submit
- >
- )}
-
-
- )
-}
diff --git a/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte b/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
deleted file mode 100644
index 01dc4b324..000000000
--- a/packages/svelte/test-app/Pages/FormComponent/PrecognitionDefaults.svelte
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
Precognition - Defaults Updates Validator
-
-
-
-
- {#if invalid('name')}
-
- {errors.name}
-
- {/if}
-
-
- {#if validating}
- Validating...
- {/if}
-
- Set Defaults
- validate('name')}>Validate Name
- Submit
-
-
diff --git a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue b/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
deleted file mode 100644
index d49acd90c..000000000
--- a/packages/vue3/test-app/Pages/FormComponent/PrecognitionDefaults.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
Precognition - Defaults Updates Validator
-
-
-
-
-
- {{ errors.name }}
-
-
-
- Validating...
-
- Set Defaults
- Validate Name
- Submit
-
-
-
diff --git a/tests/form-component.spec.ts b/tests/form-component.spec.ts
index 038e6fe2f..94ac7ebc9 100644
--- a/tests/form-component.spec.ts
+++ b/tests/form-component.spec.ts
@@ -1957,28 +1957,6 @@ test.describe('Form Component', () => {
const cancelledRequestError = await requests.failed[0].failure()?.errorText
expect(cancelledRequestError).toBe('net::ERR_ABORTED')
})
-
- test('defaults() updates validator data as well', async ({ page }) => {
- await page.goto('/form-component/precognition-defaults')
-
- await page.fill('#name-input', 'John')
- await page.locator('#name-input').blur()
- await page.click('button:has-text("Validate Name")')
-
- await expect(page.getByText('Validating...')).toBeVisible()
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Click again, should not validate again because data hasn't changed
- await expect(page.getByText('Validating...')).not.toBeVisible()
- await page.click('button:has-text("Validate Name")')
- await expect(page.getByText('Validating...')).not.toBeVisible()
-
- // Now change default to a different value, should not validate because value matches new default
- await page.fill('#name-input', 'Johnny')
- await page.click('button:has-text("Set Defaults")')
- await page.click('button:has-text("Validate Name")')
- await expect(page.getByText('Validating...')).not.toBeVisible()
- })
})
test.describe('React', () => {
From b3edfa2f1b65726f08f668df92dfc896ce6253bf Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 16:01:20 +0100
Subject: [PATCH 073/145] playgrounds
---
.../js/Pages/FormComponentPrecognition.tsx | 18 ++++++----------
.../js/Pages/FormComponentPrecognition.svelte | 19 +++++++----------
.../js/Pages/FormComponentPrecognition.svelte | 21 +++++++------------
.../js/Pages/FormComponentPrecognition.vue | 18 ++++++----------
4 files changed, 27 insertions(+), 49 deletions(-)
diff --git a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
index f64cb7139..f5085fbf6 100644
--- a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
+++ b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
@@ -1,3 +1,4 @@
+import { FormComponentMethods } from '@inertiajs/core'
import { Form, Head } from '@inertiajs/react'
import { useState } from 'react'
import Layout from '../Components/Layout'
@@ -7,26 +8,22 @@ const FormComponentPrecognition = () => {
success: false,
error: false,
finish: false,
- exception: false,
- exceptionMessage: '',
})
- const validateWithCallbacks = (validate) => {
+ const validateWithCallbacks = (validate: FormComponentMethods['validate']) => {
setCallbacks({
success: false,
error: false,
finish: false,
- exception: false,
- exceptionMessage: '',
})
validate('name', {
- onSuccess: () => setCallbacks((prev) => ({ ...prev, success: true })),
- onError: () => setCallbacks((prev) => ({ ...prev, error: true })),
+ onPrecognitionSuccess: () => setCallbacks((prev) => ({ ...prev, success: true })),
+ onValidationError: () => setCallbacks((prev) => ({ ...prev, error: true })),
onFinish: () => setCallbacks((prev) => ({ ...prev, finish: true })),
- onBefore: (newReq, oldReq) => {
+ onBeforeValidation: (newReq, oldReq) => {
// Prevent validation if name is 'block'
- if (newReq.data.name === 'block') {
+ if (newReq.data?.name === 'block') {
alert('Validation blocked by onBefore!')
return false
}
@@ -248,9 +245,6 @@ const FormComponentPrecognition = () => {
{callbacks.success && onPrecognitionSuccess called!
}
{callbacks.error && onValidationError called!
}
{callbacks.finish && onFinish called!
}
- {callbacks.exception && (
- onException: {callbacks.exceptionMessage}
- )}
)}
diff --git a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
index 583d22296..407450ff2 100644
--- a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
@@ -3,6 +3,8 @@
-
@@ -11,26 +12,22 @@ const callbacks = ref({
success: false,
error: false,
finish: false,
- exception: false,
- exceptionMessage: '',
})
-const validateWithCallbacks = (validate) => {
+const validateWithCallbacks = (validate: FormComponentMethods['validate']) => {
callbacks.value = {
success: false,
error: false,
finish: false,
- exception: false,
- exceptionMessage: '',
}
validate('name', {
- onSuccess: () => (callbacks.value.success = true),
- onError: () => (callbacks.value.error = true),
+ onPrecognitionSuccess: () => (callbacks.value.success = true),
+ onValidationError: () => (callbacks.value.error = true),
onFinish: () => (callbacks.value.finish = true),
- onBefore: (newReq, oldReq) => {
+ onBeforeValidation: (newReq, oldReq) => {
// Prevent validation if name is 'block'
- if (newReq.data.name === 'block') {
+ if (newReq.data?.name === 'block') {
alert('Validation blocked by onBefore!')
return false
}
@@ -212,9 +209,6 @@ const validateTimeout = ref(1500)
onPrecognitionSuccess called!
onValidationError called!
onFinish called!
-
- onException: {{ callbacks.exceptionMessage }}
-
From 5ad07b7471e3a42ac09a97899010610efbee5f91 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 16:09:10 +0100
Subject: [PATCH 074/145] playgrounds
---
.../react/resources/js/Pages/FormComponentPrecognition.tsx | 2 +-
.../svelte4/resources/js/Pages/FormComponentPrecognition.svelte | 2 +-
.../svelte5/resources/js/Pages/FormComponentPrecognition.svelte | 2 +-
.../vue3/resources/js/Pages/FormComponentPrecognition.vue | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
index f5085fbf6..89d4b02cf 100644
--- a/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
+++ b/playgrounds/react/resources/js/Pages/FormComponentPrecognition.tsx
@@ -17,7 +17,7 @@ const FormComponentPrecognition = () => {
finish: false,
})
- validate('name', {
+ validate({
onPrecognitionSuccess: () => setCallbacks((prev) => ({ ...prev, success: true })),
onValidationError: () => setCallbacks((prev) => ({ ...prev, error: true })),
onFinish: () => setCallbacks((prev) => ({ ...prev, finish: true })),
diff --git a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
index 407450ff2..045e64e2e 100644
--- a/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte4/resources/js/Pages/FormComponentPrecognition.svelte
@@ -22,7 +22,7 @@
finish: false,
}
- validate('name', {
+ validate({
onPrecognitionSuccess: () => (callbacks.success = true),
onValidationError: () => (callbacks.error = true),
onFinish: () => (callbacks.finish = true),
diff --git a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
index 45c4a89f4..ea2a8c69c 100644
--- a/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
+++ b/playgrounds/svelte5/resources/js/Pages/FormComponentPrecognition.svelte
@@ -22,7 +22,7 @@
finish: false,
}
- validate('name', {
+ validate({
onPrecognitionSuccess: () => (callbacks.success = true),
onValidationError: () => (callbacks.error = true),
onFinish: () => (callbacks.finish = true),
diff --git a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
index 865e12218..7328c7a19 100644
--- a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
+++ b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
@@ -21,7 +21,7 @@ const validateWithCallbacks = (validate: FormComponentMethods['validate']) => {
finish: false,
}
- validate('name', {
+ validate({
onPrecognitionSuccess: () => (callbacks.value.success = true),
onValidationError: () => (callbacks.value.error = true),
onFinish: () => (callbacks.value.finish = true),
From a3146c977a4f33d53695545baa3b188ce75507fe Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Fri, 31 Oct 2025 16:26:51 +0100
Subject: [PATCH 075/145] move import
---
.../vue3/resources/js/Pages/FormComponentPrecognition.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
index 7328c7a19..ef102ca40 100644
--- a/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
+++ b/playgrounds/vue3/resources/js/Pages/FormComponentPrecognition.vue
@@ -1,10 +1,10 @@
+
+
+
+
+
+
+
+
+ {{ error }}
+
+
+
{{ form.errors.name }}
+
+
Name is valid!
+
+
+
+
+
+
+
+ {{ error }}
+
+
+
{{ form.errors.email }}
+
+
Email is valid!
+
+
+
Validating...
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/ArrayErrors.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/ArrayErrors.vue
new file mode 100644
index 000000000..cf9a40ccf
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/ArrayErrors.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
+
+
+ {{ form.errors.email }}
+
+
Email is valid!
+
+
+
Validating...
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/BeforeValidation.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/BeforeValidation.vue
new file mode 100644
index 000000000..e891221b0
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/BeforeValidation.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
+
+
+ {{ form.errors.email }}
+
+
Email is valid!
+
+
+
Validating...
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Callbacks.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Callbacks.vue
new file mode 100644
index 000000000..b9aa16d80
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Callbacks.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
Validating...
+
onPrecognitionSuccess called!
+
onValidationError called!
+
onFinish called!
+
+
{
+ successCalled = false
+ errorCalled = false
+ finishCalled = false
+ form.validate({
+ onPrecognitionSuccess: () => {
+ successCalled = true
+ },
+ onValidationError: () => {
+ errorCalled = true
+ },
+ onFinish: () => {
+ finishCalled = true
+ },
+ })
+ }
+ "
+ >
+ Validate
+
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Cancel.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Cancel.vue
new file mode 100644
index 000000000..f8bf59d97
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Cancel.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
Validating...
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Default.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Default.vue
new file mode 100644
index 000000000..58201ac95
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Default.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
+
+
+ {{ form.errors.email }}
+
+
Email is valid!
+
+
+
Validating...
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
new file mode 100644
index 000000000..9883699cd
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
+
(form.avatar = e.target.files[0])" />
+
+ {{ form.errors.avatar }}
+
+
Avatar is valid!
+
+
+
Validating...
+
+
+ Toggle Validate Files ({{ validateFiles ? 'enabled' : 'disabled' }})
+
+
+
Validate Both
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Headers.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Headers.vue
new file mode 100644
index 000000000..9967ee9b6
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Headers.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
Validating...
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Methods.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Methods.vue
new file mode 100644
index 000000000..77f5480b3
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Methods.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
+
+
+
+
+ {{ form.errors.email }}
+
+
+
+
Validating...
+
+
{{ form.touched('name') ? 'Name is touched' : 'Name is not touched' }}
+
{{ form.touched('email') ? 'Email is touched' : 'Email is not touched' }}
+
{{ form.touched() ? 'Form has touched fields' : 'Form has no touched fields' }}
+
+
Validate All Touched
+
Validate Name
+
Validate Name and Email
+
Touch Name and Email
+
{
+ form.touch('name')
+ form.touch('name')
+ }
+ "
+ >
+ Touch Name Twice
+
+
Reset All
+
Reset Name
+
Reset Name and Email
+
+
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Transform.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Transform.vue
new file mode 100644
index 000000000..91d0b6ea7
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Transform.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ {{ form.errors.name }}
+
+
Name is valid!
+
+
+
Validating...
+
+
diff --git a/packages/vue3/test-app/package.json b/packages/vue3/test-app/package.json
index 2750f85a8..56b8d5bef 100755
--- a/packages/vue3/test-app/package.json
+++ b/packages/vue3/test-app/package.json
@@ -7,16 +7,16 @@
"type-check": "vue-tsc --noEmit"
},
"devDependencies": {
- "@eslint/js": "^9.38.0",
+ "@eslint/js": "^9.39.1",
"@vitejs/plugin-vue": "^5.2.4",
"@vue/eslint-config-typescript": "^14.6.0",
- "eslint": "^9.38.0",
+ "eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-vue": "^10.5.1",
- "globals": "^16.4.0",
+ "globals": "^16.5.0",
"nodemon": "^3.1.10",
"typescript": "^5.9.3",
- "typescript-eslint": "^8.46.2",
+ "typescript-eslint": "^8.46.3",
"vite": "^6.4.1",
"vue": "^3.5.22",
"vue-tsc": "^2.2.12"
diff --git a/playgrounds/react/package.json b/playgrounds/react/package.json
index 8efae28f1..ffa86dc14 100644
--- a/playgrounds/react/package.json
+++ b/playgrounds/react/package.json
@@ -11,18 +11,18 @@
"@inertiajs/react": "workspace:*",
"@laravel/stream-react": "^0.3.9",
"@tailwindcss/typography": "^0.5.19",
- "@tailwindcss/vite": "^4.1.15",
+ "@tailwindcss/vite": "^4.1.16",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@vitejs/plugin-react": "^4.7.0",
"autosize": "^6.0.1",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
"marked": "^16.4.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
- "tailwindcss": "^4.1.15",
+ "tailwindcss": "^4.1.16",
"typescript": "^4.9.5",
"vite": "^6.4.1"
}
diff --git a/playgrounds/svelte4/package.json b/playgrounds/svelte4/package.json
index dfd2bbe84..a3c0f51d2 100644
--- a/playgrounds/svelte4/package.json
+++ b/playgrounds/svelte4/package.json
@@ -12,14 +12,14 @@
"@inertiajs/core": "workspace:*",
"@inertiajs/svelte": "workspace:*",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
- "@tailwindcss/vite": "^4.1.15",
+ "@tailwindcss/vite": "^4.1.16",
"@tsconfig/svelte": "^5.0.5",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
"svelte": "^4.2.20",
"svelte-check": "^4.3.3",
- "tailwindcss": "^4.1.15",
+ "tailwindcss": "^4.1.16",
"typescript": "^5.9.3",
"vite": "^5.4.21"
}
diff --git a/playgrounds/svelte5/package.json b/playgrounds/svelte5/package.json
index 43c6dc760..61ff816e3 100644
--- a/playgrounds/svelte5/package.json
+++ b/playgrounds/svelte5/package.json
@@ -12,14 +12,14 @@
"@inertiajs/core": "workspace:*",
"@inertiajs/svelte": "workspace:*",
"@sveltejs/vite-plugin-svelte": "^5.1.1",
- "@tailwindcss/vite": "^4.1.15",
+ "@tailwindcss/vite": "^4.1.16",
"@tsconfig/svelte": "^5.0.5",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
- "svelte": "^5.41.1",
+ "svelte": "^5.43.3",
"svelte-check": "^4.3.3",
- "tailwindcss": "^4.1.15",
+ "tailwindcss": "^4.1.16",
"typescript": "^5.9.3",
"vite": "^6.4.1"
}
diff --git a/playgrounds/vue3/package.json b/playgrounds/vue3/package.json
index ced3a6aa9..611843520 100644
--- a/playgrounds/vue3/package.json
+++ b/playgrounds/vue3/package.json
@@ -10,15 +10,15 @@
"@inertiajs/vue3": "workspace:*",
"@laravel/stream-vue": "^0.3.9",
"@tailwindcss/typography": "^0.5.19",
- "@tailwindcss/vite": "^4.1.15",
+ "@tailwindcss/vite": "^4.1.16",
"@vitejs/plugin-vue": "^5.2.4",
"@vue/server-renderer": "^3.5.22",
"autosize": "^6.0.1",
- "axios": "^1.12.2",
+ "axios": "^1.13.1",
"laravel-vite-plugin": "^1.3.0",
"lodash": "^4.17.21",
"marked": "^16.4.1",
- "tailwindcss": "^4.1.15",
+ "tailwindcss": "^4.1.16",
"typescript": "^5.9.3",
"vite": "^6.4.1",
"vue": "^3.5.22",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3945c6520..940c9fe8d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,10 +19,10 @@ importers:
version: 4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3))
prettier-plugin-svelte:
specifier: ^3.4.0
- version: 3.4.0(prettier@3.6.2)(svelte@5.41.1)
+ version: 3.4.0(prettier@3.6.2)(svelte@5.43.3)
prettier-plugin-tailwindcss:
specifier: ^0.6.14
- version: 0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.41.1))(prettier@3.6.2)
+ version: 0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.3))(prettier@3.6.2)
optionalDependencies:
'@rollup/rollup-linux-x64-gnu':
specifier: ^4.52.5
@@ -34,8 +34,11 @@ importers:
specifier: ^4.17.12
version: 4.17.12
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
@@ -59,11 +62,11 @@ importers:
specifier: ^9.4.4
version: 9.4.4
esbuild:
- specifier: ^0.25.11
- version: 0.25.11
+ specifier: ^0.25.12
+ version: 0.25.12
esbuild-node-externals:
specifier: ^1.18.0
- version: 1.18.0(esbuild@0.25.11)
+ version: 1.18.0(esbuild@0.25.12)
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -76,6 +79,9 @@ importers:
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
@@ -87,17 +93,17 @@ importers:
specifier: ^19.2.2
version: 19.2.2(@types/react@19.2.2)
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
es-check:
specifier: ^9.4.4
version: 9.4.4
esbuild:
- specifier: ^0.25.11
- version: 0.25.11
+ specifier: ^0.25.12
+ version: 0.25.12
esbuild-node-externals:
specifier: ^1.18.0
- version: 1.18.0(esbuild@0.25.11)
+ version: 1.18.0(esbuild@0.25.12)
react:
specifier: ^19.2.0
version: 19.2.0
@@ -115,7 +121,7 @@ importers:
version: link:..
'@vitejs/plugin-react':
specifier: ^4.7.0
- version: 4.7.0(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 4.7.0(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
react:
specifier: ^19.2.0
version: 19.2.0
@@ -124,11 +130,11 @@ importers:
version: 19.2.0(react@19.2.0)
devDependencies:
'@eslint/js':
- specifier: ^9.38.0
- version: 9.38.0
+ specifier: ^9.39.1
+ version: 9.39.1
'@types/node':
- specifier: ^24.9.1
- version: 24.9.1
+ specifier: ^24.10.0
+ version: 24.10.0
'@types/react':
specifier: ^19.2.2
version: 19.2.2
@@ -136,20 +142,20 @@ importers:
specifier: ^19.2.2
version: 19.2.2(@types/react@19.2.2)
eslint:
- specifier: ^9.38.0
- version: 9.38.0(jiti@2.6.1)
+ specifier: ^9.39.1
+ version: 9.39.1(jiti@2.6.1)
eslint-config-prettier:
specifier: ^10.1.8
- version: 10.1.8(eslint@9.38.0(jiti@2.6.1))
+ version: 10.1.8(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react:
specifier: ^7.37.5
- version: 7.37.5(eslint@9.38.0(jiti@2.6.1))
+ version: 7.37.5(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react-hooks:
specifier: ^5.2.0
- version: 5.2.0(eslint@9.38.0(jiti@2.6.1))
+ version: 5.2.0(eslint@9.39.1(jiti@2.6.1))
globals:
- specifier: ^16.4.0
- version: 16.4.0
+ specifier: ^16.5.0
+ version: 16.5.0
nodemon:
specifier: ^3.1.10
version: 3.1.10
@@ -157,11 +163,11 @@ importers:
specifier: ^5.9.3
version: 5.9.3
typescript-eslint:
- specifier: ^8.46.2
- version: 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
+ specifier: ^8.46.3
+ version: 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
vite:
specifier: ^6.4.1
- version: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
packages/svelte:
dependencies:
@@ -171,25 +177,28 @@ importers:
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^3.3.1
- version: 3.3.1(@sveltejs/kit@2.47.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))
+ version: 3.3.1(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))
'@sveltejs/kit':
- specifier: ^2.47.2
- version: 2.47.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ specifier: ^2.48.4
+ version: 2.48.4(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
'@sveltejs/package':
specifier: ^2.5.4
version: 2.5.4(svelte@4.2.20)(typescript@5.9.3)
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.2
- version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
es-check:
specifier: ^9.4.4
version: 9.4.4
@@ -210,7 +219,7 @@ importers:
version: 5.9.3
vite:
specifier: ^5.4.21
- version: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
packages/svelte/test-app:
dependencies:
@@ -222,26 +231,26 @@ importers:
version: link:..
devDependencies:
'@eslint/js':
- specifier: ^9.38.0
- version: 9.38.0
+ specifier: ^9.39.1
+ version: 9.39.1
'@sveltejs/eslint-config':
specifier: ^8.3.4
- version: 8.3.4(@stylistic/eslint-plugin-js@4.4.1(eslint@9.38.0(jiti@2.6.1)))(eslint-config-prettier@10.1.8(eslint@9.38.0(jiti@2.6.1)))(eslint-plugin-n@17.23.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-plugin-svelte@3.12.5(eslint@9.38.0(jiti@2.6.1))(svelte@4.2.20))(eslint@9.38.0(jiti@2.6.1))(typescript-eslint@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(typescript@5.9.3)
+ version: 8.3.4(@stylistic/eslint-plugin-js@4.4.1(eslint@9.39.1(jiti@2.6.1)))(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-n@17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-plugin-svelte@3.13.0(eslint@9.39.1(jiti@2.6.1))(svelte@4.2.20))(eslint@9.39.1(jiti@2.6.1))(typescript-eslint@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(typescript@5.9.3)
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.2
- version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
'@tsconfig/svelte':
specifier: ^5.0.5
version: 5.0.5
eslint:
- specifier: ^9.38.0
- version: 9.38.0(jiti@2.6.1)
+ specifier: ^9.39.1
+ version: 9.39.1(jiti@2.6.1)
eslint-plugin-svelte:
- specifier: ^3.12.5
- version: 3.12.5(eslint@9.38.0(jiti@2.6.1))(svelte@4.2.20)
+ specifier: ^3.13.0
+ version: 3.13.0(eslint@9.39.1(jiti@2.6.1))(svelte@4.2.20)
globals:
- specifier: ^16.4.0
- version: 16.4.0
+ specifier: ^16.5.0
+ version: 16.5.0
nodemon:
specifier: ^3.1.10
version: 3.1.10
@@ -255,11 +264,11 @@ importers:
specifier: ^5.9.3
version: 5.9.3
typescript-eslint:
- specifier: ^8.46.2
- version: 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
+ specifier: ^8.46.3
+ version: 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
vite:
specifier: ^5.4.21
- version: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
packages/vue3:
dependencies:
@@ -269,25 +278,28 @@ importers:
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
+ laravel-precognition:
+ specifier: ^0.7.3
+ version: 0.7.3
lodash-es:
specifier: ^4.17.21
version: 4.17.21
devDependencies:
'@types/node':
- specifier: ^22.18.12
- version: 22.18.12
+ specifier: ^22.19.0
+ version: 22.19.0
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
es-check:
specifier: ^9.4.4
version: 9.4.4
esbuild:
- specifier: ^0.25.11
- version: 0.25.11
+ specifier: ^0.25.12
+ version: 0.25.12
esbuild-node-externals:
specifier: ^1.18.0
- version: 1.18.0(esbuild@0.25.11)
+ version: 1.18.0(esbuild@0.25.12)
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -305,26 +317,26 @@ importers:
version: link:..
devDependencies:
'@eslint/js':
- specifier: ^9.38.0
- version: 9.38.0
+ specifier: ^9.39.1
+ version: 9.39.1
'@vitejs/plugin-vue':
specifier: ^5.2.4
- version: 5.2.4(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))
+ version: 5.2.4(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))
'@vue/eslint-config-typescript':
specifier: ^14.6.0
- version: 14.6.0(eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@2.6.1))))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
+ version: 14.6.0(eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1))))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
eslint:
- specifier: ^9.38.0
- version: 9.38.0(jiti@2.6.1)
+ specifier: ^9.39.1
+ version: 9.39.1(jiti@2.6.1)
eslint-config-prettier:
specifier: ^10.1.8
- version: 10.1.8(eslint@9.38.0(jiti@2.6.1))
+ version: 10.1.8(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-vue:
specifier: ^10.5.1
- version: 10.5.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@2.6.1)))
+ version: 10.5.1(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)))
globals:
- specifier: ^16.4.0
- version: 16.4.0
+ specifier: ^16.5.0
+ version: 16.5.0
nodemon:
specifier: ^3.1.10
version: 3.1.10
@@ -332,11 +344,11 @@ importers:
specifier: ^5.9.3
version: 5.9.3
typescript-eslint:
- specifier: ^8.46.2
- version: 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
+ specifier: ^8.46.3
+ version: 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
vite:
specifier: ^6.4.1
- version: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vue:
specifier: ^3.5.22
version: 3.5.22(typescript@5.9.3)
@@ -357,10 +369,10 @@ importers:
version: 0.3.9(react@19.2.0)
'@tailwindcss/typography':
specifier: ^0.5.19
- version: 0.5.19(tailwindcss@4.1.15)
+ version: 0.5.19(tailwindcss@4.1.16)
'@tailwindcss/vite':
- specifier: ^4.1.15
- version: 4.1.15(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@types/react':
specifier: ^19.2.2
version: 19.2.2
@@ -369,16 +381,16 @@ importers:
version: 19.2.2(@types/react@19.2.2)
'@vitejs/plugin-react':
specifier: ^4.7.0
- version: 4.7.0(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 4.7.0(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
autosize:
specifier: ^6.0.1
version: 6.0.1
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 1.3.0(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
@@ -392,14 +404,14 @@ importers:
specifier: ^19.2.0
version: 19.2.0(react@19.2.0)
tailwindcss:
- specifier: ^4.1.15
- version: 4.1.15
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
specifier: ^4.9.5
version: 4.9.5
vite:
specifier: ^6.4.1
- version: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
playgrounds/svelte4:
devDependencies:
@@ -411,19 +423,19 @@ importers:
version: link:../../packages/svelte
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.2
- version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
'@tailwindcss/vite':
- specifier: ^4.1.15
- version: 4.1.15(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
'@tsconfig/svelte':
specifier: ^5.0.5
version: 5.0.5
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 1.3.0(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
@@ -434,14 +446,14 @@ importers:
specifier: ^4.3.3
version: 4.3.3(picomatch@4.0.3)(svelte@4.2.20)(typescript@5.9.3)
tailwindcss:
- specifier: ^4.1.15
- version: 4.1.15
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^5.4.21
- version: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
playgrounds/svelte5:
devDependencies:
@@ -453,37 +465,37 @@ importers:
version: link:../../packages/svelte
'@sveltejs/vite-plugin-svelte':
specifier: ^5.1.1
- version: 5.1.1(svelte@5.41.1)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 5.1.1(svelte@5.43.3)(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@tailwindcss/vite':
- specifier: ^4.1.15
- version: 4.1.15(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@tsconfig/svelte':
specifier: ^5.0.5
version: 5.0.5
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 1.3.0(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
svelte:
- specifier: ^5.41.1
- version: 5.41.1
+ specifier: ^5.43.3
+ version: 5.43.3
svelte-check:
specifier: ^4.3.3
- version: 4.3.3(picomatch@4.0.3)(svelte@5.41.1)(typescript@5.9.3)
+ version: 4.3.3(picomatch@4.0.3)(svelte@5.43.3)(typescript@5.9.3)
tailwindcss:
- specifier: ^4.1.15
- version: 4.1.15
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^6.4.1
- version: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
playgrounds/vue3:
devDependencies:
@@ -498,13 +510,13 @@ importers:
version: 0.3.9(vue@3.5.22(typescript@5.9.3))
'@tailwindcss/typography':
specifier: ^0.5.19
- version: 0.5.19(tailwindcss@4.1.15)
+ version: 0.5.19(tailwindcss@4.1.16)
'@tailwindcss/vite':
- specifier: ^4.1.15
- version: 4.1.15(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ specifier: ^4.1.16
+ version: 4.1.16(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
'@vitejs/plugin-vue':
specifier: ^5.2.4
- version: 5.2.4(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))
+ version: 5.2.4(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))
'@vue/server-renderer':
specifier: ^3.5.22
version: 3.5.22(vue@3.5.22(typescript@5.9.3))
@@ -512,11 +524,11 @@ importers:
specifier: ^6.0.1
version: 6.0.1
axios:
- specifier: ^1.12.2
- version: 1.12.2
+ specifier: ^1.13.1
+ version: 1.13.1
laravel-vite-plugin:
specifier: ^1.3.0
- version: 1.3.0(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ version: 1.3.0(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
lodash:
specifier: ^4.17.21
version: 4.17.21
@@ -524,14 +536,14 @@ importers:
specifier: ^16.4.1
version: 16.4.1
tailwindcss:
- specifier: ^4.1.15
- version: 4.1.15
+ specifier: ^4.1.16
+ version: 4.1.16
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^6.4.1
- version: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ version: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vue:
specifier: ^3.5.22
version: 3.5.22(typescript@5.9.3)
@@ -573,16 +585,16 @@ packages:
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
- '@babel/compat-data@7.28.4':
- resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
+ '@babel/compat-data@7.28.5':
+ resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==}
engines: {node: '>=6.9.0'}
- '@babel/core@7.28.4':
- resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
+ '@babel/core@7.28.5':
+ resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==}
engines: {node: '>=6.9.0'}
- '@babel/generator@7.28.3':
- resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
+ '@babel/generator@7.28.5':
+ resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.27.2':
@@ -611,8 +623,8 @@ packages:
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.27.1':
- resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-option@7.27.1':
@@ -627,8 +639,8 @@ packages:
resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==}
engines: {node: '>=6.9.0'}
- '@babel/parser@7.28.4':
- resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
+ '@babel/parser@7.28.5':
+ resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -648,12 +660,12 @@ packages:
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
- '@babel/traverse@7.28.4':
- resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
+ '@babel/traverse@7.28.5':
+ resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.28.4':
- resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
+ '@babel/types@7.28.5':
+ resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
engines: {node: '>=6.9.0'}
'@colors/colors@1.6.0':
@@ -669,8 +681,8 @@ packages:
cpu: [ppc64]
os: [aix]
- '@esbuild/aix-ppc64@0.25.11':
- resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
+ '@esbuild/aix-ppc64@0.25.12':
+ resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
@@ -681,8 +693,8 @@ packages:
cpu: [arm64]
os: [android]
- '@esbuild/android-arm64@0.25.11':
- resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==}
+ '@esbuild/android-arm64@0.25.12':
+ resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
@@ -693,8 +705,8 @@ packages:
cpu: [arm]
os: [android]
- '@esbuild/android-arm@0.25.11':
- resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==}
+ '@esbuild/android-arm@0.25.12':
+ resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
@@ -705,8 +717,8 @@ packages:
cpu: [x64]
os: [android]
- '@esbuild/android-x64@0.25.11':
- resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==}
+ '@esbuild/android-x64@0.25.12':
+ resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
@@ -717,8 +729,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-arm64@0.25.11':
- resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==}
+ '@esbuild/darwin-arm64@0.25.12':
+ resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
@@ -729,8 +741,8 @@ packages:
cpu: [x64]
os: [darwin]
- '@esbuild/darwin-x64@0.25.11':
- resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==}
+ '@esbuild/darwin-x64@0.25.12':
+ resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
@@ -741,8 +753,8 @@ packages:
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-arm64@0.25.11':
- resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==}
+ '@esbuild/freebsd-arm64@0.25.12':
+ resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
@@ -753,8 +765,8 @@ packages:
cpu: [x64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.25.11':
- resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==}
+ '@esbuild/freebsd-x64@0.25.12':
+ resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
@@ -765,8 +777,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm64@0.25.11':
- resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==}
+ '@esbuild/linux-arm64@0.25.12':
+ resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
@@ -777,8 +789,8 @@ packages:
cpu: [arm]
os: [linux]
- '@esbuild/linux-arm@0.25.11':
- resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==}
+ '@esbuild/linux-arm@0.25.12':
+ resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
@@ -789,8 +801,8 @@ packages:
cpu: [ia32]
os: [linux]
- '@esbuild/linux-ia32@0.25.11':
- resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==}
+ '@esbuild/linux-ia32@0.25.12':
+ resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
@@ -801,8 +813,8 @@ packages:
cpu: [loong64]
os: [linux]
- '@esbuild/linux-loong64@0.25.11':
- resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==}
+ '@esbuild/linux-loong64@0.25.12':
+ resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
@@ -813,8 +825,8 @@ packages:
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-mips64el@0.25.11':
- resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==}
+ '@esbuild/linux-mips64el@0.25.12':
+ resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
@@ -825,8 +837,8 @@ packages:
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-ppc64@0.25.11':
- resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==}
+ '@esbuild/linux-ppc64@0.25.12':
+ resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
@@ -837,8 +849,8 @@ packages:
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-riscv64@0.25.11':
- resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==}
+ '@esbuild/linux-riscv64@0.25.12':
+ resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
@@ -849,8 +861,8 @@ packages:
cpu: [s390x]
os: [linux]
- '@esbuild/linux-s390x@0.25.11':
- resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==}
+ '@esbuild/linux-s390x@0.25.12':
+ resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
@@ -861,14 +873,14 @@ packages:
cpu: [x64]
os: [linux]
- '@esbuild/linux-x64@0.25.11':
- resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==}
+ '@esbuild/linux-x64@0.25.12':
+ resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
- '@esbuild/netbsd-arm64@0.25.11':
- resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==}
+ '@esbuild/netbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
@@ -879,14 +891,14 @@ packages:
cpu: [x64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.25.11':
- resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==}
+ '@esbuild/netbsd-x64@0.25.12':
+ resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
- '@esbuild/openbsd-arm64@0.25.11':
- resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==}
+ '@esbuild/openbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
@@ -897,14 +909,14 @@ packages:
cpu: [x64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.25.11':
- resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==}
+ '@esbuild/openbsd-x64@0.25.12':
+ resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/openharmony-arm64@0.25.11':
- resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==}
+ '@esbuild/openharmony-arm64@0.25.12':
+ resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
@@ -915,8 +927,8 @@ packages:
cpu: [x64]
os: [sunos]
- '@esbuild/sunos-x64@0.25.11':
- resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==}
+ '@esbuild/sunos-x64@0.25.12':
+ resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
@@ -927,8 +939,8 @@ packages:
cpu: [arm64]
os: [win32]
- '@esbuild/win32-arm64@0.25.11':
- resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==}
+ '@esbuild/win32-arm64@0.25.12':
+ resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
@@ -939,8 +951,8 @@ packages:
cpu: [ia32]
os: [win32]
- '@esbuild/win32-ia32@0.25.11':
- resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==}
+ '@esbuild/win32-ia32@0.25.12':
+ resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
@@ -951,8 +963,8 @@ packages:
cpu: [x64]
os: [win32]
- '@esbuild/win32-x64@0.25.11':
- resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==}
+ '@esbuild/win32-x64@0.25.12':
+ resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
@@ -963,20 +975,20 @@ packages:
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
- '@eslint-community/regexpp@4.12.1':
- resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint/config-array@0.21.1':
resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/config-helpers@0.4.1':
- resolution: {integrity: sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==}
+ '@eslint/config-helpers@0.4.2':
+ resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/core@0.16.0':
- resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==}
+ '@eslint/core@0.17.0':
+ resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@0.4.3':
@@ -987,16 +999,16 @@ packages:
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@9.38.0':
- resolution: {integrity: sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==}
+ '@eslint/js@9.39.1':
+ resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.7':
resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/plugin-kit@0.4.0':
- resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==}
+ '@eslint/plugin-kit@0.4.1':
+ resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@humanfs/core@0.19.1':
@@ -1219,8 +1231,8 @@ packages:
typescript: '>= 5'
typescript-eslint: '>= 8'
- '@sveltejs/kit@2.47.2':
- resolution: {integrity: sha512-mbUomaJTiADTrq6GT4ZvQ7v1rs0S+wXGMzrjFwjARAKMEF8FpOUmz2uEJ4M9WMJMQOXCMHpKFzJfdjo9O7M22A==}
+ '@sveltejs/kit@2.48.4':
+ resolution: {integrity: sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==}
engines: {node: '>=18.13'}
hasBin: true
peerDependencies:
@@ -1269,65 +1281,65 @@ packages:
svelte: ^5.0.0
vite: ^6.0.0
- '@tailwindcss/node@4.1.15':
- resolution: {integrity: sha512-HF4+7QxATZWY3Jr8OlZrBSXmwT3Watj0OogeDvdUY/ByXJHQ+LBtqA2brDb3sBxYslIFx6UP94BJ4X6a4L9Bmw==}
+ '@tailwindcss/node@4.1.16':
+ resolution: {integrity: sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==}
- '@tailwindcss/oxide-android-arm64@4.1.15':
- resolution: {integrity: sha512-TkUkUgAw8At4cBjCeVCRMc/guVLKOU1D+sBPrHt5uVcGhlbVKxrCaCW9OKUIBv1oWkjh4GbunD/u/Mf0ql6kEA==}
+ '@tailwindcss/oxide-android-arm64@4.1.16':
+ resolution: {integrity: sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@tailwindcss/oxide-darwin-arm64@4.1.15':
- resolution: {integrity: sha512-xt5XEJpn2piMSfvd1UFN6jrWXyaKCwikP4Pidcf+yfHTSzSpYhG3dcMktjNkQO3JiLCp+0bG0HoWGvz97K162w==}
+ '@tailwindcss/oxide-darwin-arm64@4.1.16':
+ resolution: {integrity: sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@tailwindcss/oxide-darwin-x64@4.1.15':
- resolution: {integrity: sha512-TnWaxP6Bx2CojZEXAV2M01Yl13nYPpp0EtGpUrY+LMciKfIXiLL2r/SiSRpagE5Fp2gX+rflp/Os1VJDAyqymg==}
+ '@tailwindcss/oxide-darwin-x64@4.1.16':
+ resolution: {integrity: sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@tailwindcss/oxide-freebsd-x64@4.1.15':
- resolution: {integrity: sha512-quISQDWqiB6Cqhjc3iWptXVZHNVENsWoI77L1qgGEHNIdLDLFnw3/AfY7DidAiiCIkGX/MjIdB3bbBZR/G2aJg==}
+ '@tailwindcss/oxide-freebsd-x64@4.1.16':
+ resolution: {integrity: sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.15':
- resolution: {integrity: sha512-ObG76+vPlab65xzVUQbExmDU9FIeYLQ5k2LrQdR2Ud6hboR+ZobXpDoKEYXf/uOezOfIYmy2Ta3w0ejkTg9yxg==}
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16':
+ resolution: {integrity: sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.15':
- resolution: {integrity: sha512-4WbBacRmk43pkb8/xts3wnOZMDKsPFyEH/oisCm2q3aLZND25ufvJKcDUpAu0cS+CBOL05dYa8D4U5OWECuH/Q==}
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.16':
+ resolution: {integrity: sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-musl@4.1.15':
- resolution: {integrity: sha512-AbvmEiteEj1nf42nE8skdHv73NoR+EwXVSgPY6l39X12Ex8pzOwwfi3Kc8GAmjsnsaDEbk+aj9NyL3UeyHcTLg==}
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.16':
+ resolution: {integrity: sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-gnu@4.1.15':
- resolution: {integrity: sha512-+rzMVlvVgrXtFiS+ES78yWgKqpThgV19ISKD58Ck+YO5pO5KjyxLt7AWKsWMbY0R9yBDC82w6QVGz837AKQcHg==}
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.16':
+ resolution: {integrity: sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-musl@4.1.15':
- resolution: {integrity: sha512-fPdEy7a8eQN9qOIK3Em9D3TO1z41JScJn8yxl/76mp4sAXFDfV4YXxsiptJcOwy6bGR+70ZSwFIZhTXzQeqwQg==}
+ '@tailwindcss/oxide-linux-x64-musl@4.1.16':
+ resolution: {integrity: sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-wasm32-wasi@4.1.15':
- resolution: {integrity: sha512-sJ4yd6iXXdlgIMfIBXuVGp/NvmviEoMVWMOAGxtxhzLPp9LOj5k0pMEMZdjeMCl4C6Up+RM8T3Zgk+BMQ0bGcQ==}
+ '@tailwindcss/oxide-wasm32-wasi@4.1.16':
+ resolution: {integrity: sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
@@ -1338,20 +1350,20 @@ packages:
- '@emnapi/wasi-threads'
- tslib
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.15':
- resolution: {integrity: sha512-sJGE5faXnNQ1iXeqmRin7Ds/ru2fgCiaQZQQz3ZGIDtvbkeV85rAZ0QJFMDg0FrqsffZG96H1U9AQlNBRLsHVg==}
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.16':
+ resolution: {integrity: sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@tailwindcss/oxide-win32-x64-msvc@4.1.15':
- resolution: {integrity: sha512-NLeHE7jUV6HcFKS504bpOohyi01zPXi2PXmjFfkzTph8xRxDdxkRsXm/xDO5uV5K3brrE1cCwbUYmFUSHR3u1w==}
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.16':
+ resolution: {integrity: sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@tailwindcss/oxide@4.1.15':
- resolution: {integrity: sha512-krhX+UOOgnsUuks2SR7hFafXmLQrKxB4YyRTERuCE59JlYL+FawgaAlSkOYmDRJdf1Q+IFNDMl9iRnBW7QBDfQ==}
+ '@tailwindcss/oxide@4.1.16':
+ resolution: {integrity: sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==}
engines: {node: '>= 10'}
'@tailwindcss/typography@0.5.19':
@@ -1359,8 +1371,8 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
- '@tailwindcss/vite@4.1.15':
- resolution: {integrity: sha512-B6s60MZRTUil+xKoZoGe6i0Iar5VuW+pmcGlda2FX+guDuQ1G1sjiIy1W0frneVpeL/ZjZ4KEgWZHNrIm++2qA==}
+ '@tailwindcss/vite@4.1.16':
+ resolution: {integrity: sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==}
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
@@ -1407,11 +1419,11 @@ packages:
'@types/node@18.19.130':
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
- '@types/node@22.18.12':
- resolution: {integrity: sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==}
+ '@types/node@22.19.0':
+ resolution: {integrity: sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==}
- '@types/node@24.9.1':
- resolution: {integrity: sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==}
+ '@types/node@24.10.0':
+ resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==}
'@types/nprogress@0.2.3':
resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
@@ -1430,63 +1442,63 @@ packages:
'@types/triple-beam@1.3.5':
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
- '@typescript-eslint/eslint-plugin@8.46.2':
- resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==}
+ '@typescript-eslint/eslint-plugin@8.46.3':
+ resolution: {integrity: sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.46.2
+ '@typescript-eslint/parser': ^8.46.3
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/parser@8.46.2':
- resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==}
+ '@typescript-eslint/parser@8.46.3':
+ resolution: {integrity: sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/project-service@8.46.2':
- resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==}
+ '@typescript-eslint/project-service@8.46.3':
+ resolution: {integrity: sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/scope-manager@8.46.2':
- resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==}
+ '@typescript-eslint/scope-manager@8.46.3':
+ resolution: {integrity: sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/tsconfig-utils@8.46.2':
- resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==}
+ '@typescript-eslint/tsconfig-utils@8.46.3':
+ resolution: {integrity: sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/type-utils@8.46.2':
- resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==}
+ '@typescript-eslint/type-utils@8.46.3':
+ resolution: {integrity: sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/types@8.46.2':
- resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==}
+ '@typescript-eslint/types@8.46.3':
+ resolution: {integrity: sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.46.2':
- resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==}
+ '@typescript-eslint/typescript-estree@8.46.3':
+ resolution: {integrity: sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/utils@8.46.2':
- resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==}
+ '@typescript-eslint/utils@8.46.3':
+ resolution: {integrity: sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/visitor-keys@8.46.2':
- resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==}
+ '@typescript-eslint/visitor-keys@8.46.3':
+ resolution: {integrity: sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@vitejs/plugin-react@4.7.0':
@@ -1754,8 +1766,8 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
- axios@1.12.2:
- resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==}
+ axios@1.13.1:
+ resolution: {integrity: sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==}
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
@@ -1764,8 +1776,8 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
- baseline-browser-mapping@2.8.18:
- resolution: {integrity: sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w==}
+ baseline-browser-mapping@2.8.23:
+ resolution: {integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==}
hasBin: true
big.js@5.2.2:
@@ -1792,8 +1804,8 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
- browserslist@4.26.3:
- resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==}
+ browserslist@4.27.0:
+ resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -1824,8 +1836,8 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
- caniuse-lite@1.0.30001751:
- resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==}
+ caniuse-lite@1.0.30001753:
+ resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@@ -2011,8 +2023,8 @@ packages:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
- devalue@5.4.1:
- resolution: {integrity: sha512-YtoaOfsqjbZQKGIMRYDWKjUmSB4VJ/RElB+bXZawQAQYAo4xu08GKTMVlsZDTF6R2MbAgjcAQRPI5eIyRAT2OQ==}
+ devalue@5.4.2:
+ resolution: {integrity: sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==}
doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
@@ -2029,8 +2041,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
- electron-to-chromium@1.5.237:
- resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==}
+ electron-to-chromium@1.5.244:
+ resolution: {integrity: sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2113,8 +2125,8 @@ packages:
engines: {node: '>=12'}
hasBin: true
- esbuild@0.25.11:
- resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==}
+ esbuild@0.25.12:
+ resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
hasBin: true
@@ -2169,8 +2181,8 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
- eslint-plugin-svelte@3.12.5:
- resolution: {integrity: sha512-4KRG84eAHQfYd9OjZ1K7sCHy0nox+9KwT+s5WCCku3jTim5RV4tVENob274nCwIaApXsYPKAUAZFBxKZ3Wyfjw==}
+ eslint-plugin-svelte@3.13.0:
+ resolution: {integrity: sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.1 || ^9.0.0
@@ -2227,8 +2239,8 @@ packages:
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true
- eslint@9.38.0:
- resolution: {integrity: sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==}
+ eslint@9.39.1:
+ resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -2257,8 +2269,8 @@ packages:
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
- esrap@2.1.0:
- resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==}
+ esrap@2.1.2:
+ resolution: {integrity: sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==}
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
@@ -2433,8 +2445,8 @@ packages:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
- get-tsconfig@4.12.0:
- resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==}
+ get-tsconfig@4.13.0:
+ resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
@@ -2468,8 +2480,8 @@ packages:
resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
engines: {node: '>=18'}
- globals@16.4.0:
- resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
+ globals@16.5.0:
+ resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==}
engines: {node: '>=18'}
globalthis@1.0.4:
@@ -2769,6 +2781,9 @@ packages:
kuler@2.0.0:
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
+ laravel-precognition@0.7.3:
+ resolution: {integrity: sha512-Z97i35Q0wmsRC9WFUY2EmFS2W1F6FF/HakwZg6PqSaR9lHBgbdIs1YZpDlkChVkgcmmvbN19ujKss3ZqbjPM1g==}
+
laravel-vite-plugin@1.3.0:
resolution: {integrity: sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -2896,8 +2911,8 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
- magic-string@0.30.19:
- resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
marked@16.4.1:
resolution: {integrity: sha512-ntROs7RaN3EvWfy3EZi14H4YxmT6A5YvywfhO+0pm+cH/dnSQRmdAmoFIc3B9aiwTehyk7pESH4ofyBY+V5hZg==}
@@ -3005,8 +3020,8 @@ packages:
neo-async@2.6.2:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
- node-releases@2.0.26:
- resolution: {integrity: sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==}
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
nodemon@3.1.10:
resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==}
@@ -3477,8 +3492,8 @@ packages:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
- set-cookie-parser@2.7.1:
- resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+ set-cookie-parser@2.7.2:
+ resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
@@ -3647,16 +3662,16 @@ packages:
resolution: {integrity: sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==}
engines: {node: '>=16'}
- svelte@5.41.1:
- resolution: {integrity: sha512-0a/huwc8e2es+7KFi70esqsReRfRbrT8h1cJSY/+z1lF0yKM6TT+//HYu28Yxstr50H7ifaqZRDGd0KuKDxP7w==}
+ svelte@5.43.3:
+ resolution: {integrity: sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ==}
engines: {node: '>=18'}
table@6.9.0:
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
engines: {node: '>=10.0.0'}
- tailwindcss@4.1.15:
- resolution: {integrity: sha512-k2WLnWkYFkdpRv+Oby3EBXIyQC8/s1HOFMBUViwtAh6Z5uAozeUSMQlIsn/c6Q2iJzqG6aJT3wdPaRNj70iYxQ==}
+ tailwindcss@4.1.16:
+ resolution: {integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==}
tapable@2.3.0:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
@@ -3758,8 +3773,8 @@ packages:
typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
- typescript-eslint@8.46.2:
- resolution: {integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==}
+ typescript-eslint@8.46.3:
+ resolution: {integrity: sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -3795,8 +3810,8 @@ packages:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
- update-browserslist-db@1.1.3:
- resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ update-browserslist-db@1.1.4:
+ resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
@@ -4020,23 +4035,23 @@ snapshots:
'@babel/code-frame@7.27.1':
dependencies:
- '@babel/helper-validator-identifier': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
js-tokens: 4.0.0
picocolors: 1.1.1
- '@babel/compat-data@7.28.4': {}
+ '@babel/compat-data@7.28.5': {}
- '@babel/core@7.28.4':
+ '@babel/core@7.28.5':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/generator': 7.28.3
+ '@babel/generator': 7.28.5
'@babel/helper-compilation-targets': 7.27.2
- '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5)
'@babel/helpers': 7.28.4
- '@babel/parser': 7.28.4
+ '@babel/parser': 7.28.5
'@babel/template': 7.27.2
- '@babel/traverse': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
debug: 4.4.3(supports-color@5.5.0)
@@ -4046,19 +4061,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/generator@7.28.3':
+ '@babel/generator@7.28.5':
dependencies:
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
jsesc: 3.1.0
'@babel/helper-compilation-targets@7.27.2':
dependencies:
- '@babel/compat-data': 7.28.4
+ '@babel/compat-data': 7.28.5
'@babel/helper-validator-option': 7.27.1
- browserslist: 4.26.3
+ browserslist: 4.27.0
lru-cache: 5.1.1
semver: 6.3.1
@@ -4066,17 +4081,17 @@ snapshots:
'@babel/helper-module-imports@7.27.1':
dependencies:
- '@babel/traverse': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)':
dependencies:
- '@babel/core': 7.28.4
+ '@babel/core': 7.28.5
'@babel/helper-module-imports': 7.27.1
- '@babel/helper-validator-identifier': 7.27.1
- '@babel/traverse': 7.28.4
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.28.5
transitivePeerDependencies:
- supports-color
@@ -4084,58 +4099,58 @@ snapshots:
'@babel/helper-string-parser@7.27.1': {}
- '@babel/helper-validator-identifier@7.27.1': {}
+ '@babel/helper-validator-identifier@7.28.5': {}
'@babel/helper-validator-option@7.27.1': {}
'@babel/helpers@7.28.4':
dependencies:
'@babel/template': 7.27.2
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
'@babel/highlight@7.25.9':
dependencies:
- '@babel/helper-validator-identifier': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
chalk: 2.4.2
js-tokens: 4.0.0
picocolors: 1.1.1
- '@babel/parser@7.28.4':
+ '@babel/parser@7.28.5':
dependencies:
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
- '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)':
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)':
dependencies:
- '@babel/core': 7.28.4
+ '@babel/core': 7.28.5
'@babel/helper-plugin-utils': 7.27.1
- '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)':
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)':
dependencies:
- '@babel/core': 7.28.4
+ '@babel/core': 7.28.5
'@babel/helper-plugin-utils': 7.27.1
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
- '@babel/traverse@7.28.4':
+ '@babel/traverse@7.28.5':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/generator': 7.28.3
+ '@babel/generator': 7.28.5
'@babel/helper-globals': 7.28.0
- '@babel/parser': 7.28.4
+ '@babel/parser': 7.28.5
'@babel/template': 7.27.2
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
- '@babel/types@7.28.4':
+ '@babel/types@7.28.5':
dependencies:
'@babel/helper-string-parser': 7.27.1
- '@babel/helper-validator-identifier': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
'@colors/colors@1.6.0': {}
@@ -4148,156 +4163,156 @@ snapshots:
'@esbuild/aix-ppc64@0.21.5':
optional: true
- '@esbuild/aix-ppc64@0.25.11':
+ '@esbuild/aix-ppc64@0.25.12':
optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
- '@esbuild/android-arm64@0.25.11':
+ '@esbuild/android-arm64@0.25.12':
optional: true
'@esbuild/android-arm@0.21.5':
optional: true
- '@esbuild/android-arm@0.25.11':
+ '@esbuild/android-arm@0.25.12':
optional: true
'@esbuild/android-x64@0.21.5':
optional: true
- '@esbuild/android-x64@0.25.11':
+ '@esbuild/android-x64@0.25.12':
optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
- '@esbuild/darwin-arm64@0.25.11':
+ '@esbuild/darwin-arm64@0.25.12':
optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
- '@esbuild/darwin-x64@0.25.11':
+ '@esbuild/darwin-x64@0.25.12':
optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
- '@esbuild/freebsd-arm64@0.25.11':
+ '@esbuild/freebsd-arm64@0.25.12':
optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
- '@esbuild/freebsd-x64@0.25.11':
+ '@esbuild/freebsd-x64@0.25.12':
optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
- '@esbuild/linux-arm64@0.25.11':
+ '@esbuild/linux-arm64@0.25.12':
optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
- '@esbuild/linux-arm@0.25.11':
+ '@esbuild/linux-arm@0.25.12':
optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
- '@esbuild/linux-ia32@0.25.11':
+ '@esbuild/linux-ia32@0.25.12':
optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
- '@esbuild/linux-loong64@0.25.11':
+ '@esbuild/linux-loong64@0.25.12':
optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
- '@esbuild/linux-mips64el@0.25.11':
+ '@esbuild/linux-mips64el@0.25.12':
optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
- '@esbuild/linux-ppc64@0.25.11':
+ '@esbuild/linux-ppc64@0.25.12':
optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
- '@esbuild/linux-riscv64@0.25.11':
+ '@esbuild/linux-riscv64@0.25.12':
optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
- '@esbuild/linux-s390x@0.25.11':
+ '@esbuild/linux-s390x@0.25.12':
optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
- '@esbuild/linux-x64@0.25.11':
+ '@esbuild/linux-x64@0.25.12':
optional: true
- '@esbuild/netbsd-arm64@0.25.11':
+ '@esbuild/netbsd-arm64@0.25.12':
optional: true
'@esbuild/netbsd-x64@0.21.5':
optional: true
- '@esbuild/netbsd-x64@0.25.11':
+ '@esbuild/netbsd-x64@0.25.12':
optional: true
- '@esbuild/openbsd-arm64@0.25.11':
+ '@esbuild/openbsd-arm64@0.25.12':
optional: true
'@esbuild/openbsd-x64@0.21.5':
optional: true
- '@esbuild/openbsd-x64@0.25.11':
+ '@esbuild/openbsd-x64@0.25.12':
optional: true
- '@esbuild/openharmony-arm64@0.25.11':
+ '@esbuild/openharmony-arm64@0.25.12':
optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
- '@esbuild/sunos-x64@0.25.11':
+ '@esbuild/sunos-x64@0.25.12':
optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
- '@esbuild/win32-arm64@0.25.11':
+ '@esbuild/win32-arm64@0.25.12':
optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
- '@esbuild/win32-ia32@0.25.11':
+ '@esbuild/win32-ia32@0.25.12':
optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
- '@esbuild/win32-x64@0.25.11':
+ '@esbuild/win32-x64@0.25.12':
optional: true
- '@eslint-community/eslint-utils@4.9.0(eslint@9.38.0(jiti@2.6.1))':
+ '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))':
dependencies:
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
eslint-visitor-keys: 3.4.3
- '@eslint-community/regexpp@4.12.1': {}
+ '@eslint-community/regexpp@4.12.2': {}
'@eslint/config-array@0.21.1':
dependencies:
@@ -4307,11 +4322,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/config-helpers@0.4.1':
+ '@eslint/config-helpers@0.4.2':
dependencies:
- '@eslint/core': 0.16.0
+ '@eslint/core': 0.17.0
- '@eslint/core@0.16.0':
+ '@eslint/core@0.17.0':
dependencies:
'@types/json-schema': 7.0.15
@@ -4343,13 +4358,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/js@9.38.0': {}
+ '@eslint/js@9.39.1': {}
'@eslint/object-schema@2.1.7': {}
- '@eslint/plugin-kit@0.4.0':
+ '@eslint/plugin-kit@0.4.1':
dependencies:
- '@eslint/core': 0.16.0
+ '@eslint/core': 0.17.0
levn: 0.4.1
'@humanfs/core@0.19.1': {}
@@ -4499,9 +4514,9 @@ snapshots:
'@standard-schema/spec@1.0.0': {}
- '@stylistic/eslint-plugin-js@4.4.1(eslint@9.38.0(jiti@2.6.1))':
+ '@stylistic/eslint-plugin-js@4.4.1(eslint@9.39.1(jiti@2.6.1))':
dependencies:
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
eslint-visitor-keys: 4.2.1
espree: 10.4.0
@@ -4509,40 +4524,40 @@ snapshots:
dependencies:
acorn: 8.15.0
- '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.47.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))':
+ '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))':
dependencies:
- '@sveltejs/kit': 2.47.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/kit': 2.48.4(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
import-meta-resolve: 4.2.0
- '@sveltejs/eslint-config@8.3.4(@stylistic/eslint-plugin-js@4.4.1(eslint@9.38.0(jiti@2.6.1)))(eslint-config-prettier@10.1.8(eslint@9.38.0(jiti@2.6.1)))(eslint-plugin-n@17.23.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-plugin-svelte@3.12.5(eslint@9.38.0(jiti@2.6.1))(svelte@4.2.20))(eslint@9.38.0(jiti@2.6.1))(typescript-eslint@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(typescript@5.9.3)':
+ '@sveltejs/eslint-config@8.3.4(@stylistic/eslint-plugin-js@4.4.1(eslint@9.39.1(jiti@2.6.1)))(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-n@17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-plugin-svelte@3.13.0(eslint@9.39.1(jiti@2.6.1))(svelte@4.2.20))(eslint@9.39.1(jiti@2.6.1))(typescript-eslint@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(typescript@5.9.3)':
dependencies:
- '@stylistic/eslint-plugin-js': 4.4.1(eslint@9.38.0(jiti@2.6.1))
- eslint: 9.38.0(jiti@2.6.1)
- eslint-config-prettier: 10.1.8(eslint@9.38.0(jiti@2.6.1))
- eslint-plugin-n: 17.23.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- eslint-plugin-svelte: 3.12.5(eslint@9.38.0(jiti@2.6.1))(svelte@4.2.20)
+ '@stylistic/eslint-plugin-js': 4.4.1(eslint@9.39.1(jiti@2.6.1))
+ eslint: 9.39.1(jiti@2.6.1)
+ eslint-config-prettier: 10.1.8(eslint@9.39.1(jiti@2.6.1))
+ eslint-plugin-n: 17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ eslint-plugin-svelte: 3.13.0(eslint@9.39.1(jiti@2.6.1))(svelte@4.2.20)
globals: 15.15.0
typescript: 5.9.3
- typescript-eslint: 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
+ typescript-eslint: 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
- '@sveltejs/kit@2.47.2(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
'@standard-schema/spec': 1.0.0
'@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0)
- '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
'@types/cookie': 0.6.0
acorn: 8.15.0
cookie: 0.6.0
- devalue: 5.4.1
+ devalue: 5.4.2
esm-env: 1.2.2
kleur: 4.1.5
- magic-string: 0.30.19
+ magic-string: 0.30.21
mrmime: 2.0.1
sade: 1.8.1
- set-cookie-parser: 2.7.1
+ set-cookie-parser: 2.7.2
sirv: 3.0.2
svelte: 4.2.20
- vite: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
'@sveltejs/package@2.5.4(svelte@4.2.20)(typescript@5.9.3)':
dependencies:
@@ -4555,153 +4570,153 @@ snapshots:
transitivePeerDependencies:
- typescript
- '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
debug: 4.4.3(supports-color@5.5.0)
svelte: 4.2.20
- vite: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.41.1)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.41.1)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.3)(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.3)(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.41.1)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.43.3)(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
debug: 4.4.3(supports-color@5.5.0)
- svelte: 5.41.1
- vite: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ svelte: 5.43.3
+ vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@4.2.20)(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
debug: 4.4.3(supports-color@5.5.0)
deepmerge: 4.3.1
kleur: 4.1.5
- magic-string: 0.30.19
+ magic-string: 0.30.21
svelte: 4.2.20
svelte-hmr: 0.16.0(svelte@4.2.20)
- vite: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
- vitefu: 0.2.5(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))
+ vite: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
+ vitefu: 0.2.5(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.41.1)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.3)(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.41.1)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.41.1)(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.3)(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)))(svelte@5.43.3)(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
debug: 4.4.3(supports-color@5.5.0)
deepmerge: 4.3.1
kleur: 4.1.5
- magic-string: 0.30.19
- svelte: 5.41.1
- vite: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
- vitefu: 1.1.1(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
+ magic-string: 0.30.21
+ svelte: 5.43.3
+ vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vitefu: 1.1.1(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))
transitivePeerDependencies:
- supports-color
- '@tailwindcss/node@4.1.15':
+ '@tailwindcss/node@4.1.16':
dependencies:
'@jridgewell/remapping': 2.3.5
enhanced-resolve: 5.18.3
jiti: 2.6.1
lightningcss: 1.30.2
- magic-string: 0.30.19
+ magic-string: 0.30.21
source-map-js: 1.2.1
- tailwindcss: 4.1.15
+ tailwindcss: 4.1.16
- '@tailwindcss/oxide-android-arm64@4.1.15':
+ '@tailwindcss/oxide-android-arm64@4.1.16':
optional: true
- '@tailwindcss/oxide-darwin-arm64@4.1.15':
+ '@tailwindcss/oxide-darwin-arm64@4.1.16':
optional: true
- '@tailwindcss/oxide-darwin-x64@4.1.15':
+ '@tailwindcss/oxide-darwin-x64@4.1.16':
optional: true
- '@tailwindcss/oxide-freebsd-x64@4.1.15':
+ '@tailwindcss/oxide-freebsd-x64@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.15':
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.15':
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-arm64-musl@4.1.15':
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-x64-gnu@4.1.15':
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.16':
optional: true
- '@tailwindcss/oxide-linux-x64-musl@4.1.15':
+ '@tailwindcss/oxide-linux-x64-musl@4.1.16':
optional: true
- '@tailwindcss/oxide-wasm32-wasi@4.1.15':
+ '@tailwindcss/oxide-wasm32-wasi@4.1.16':
optional: true
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.15':
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.16':
optional: true
- '@tailwindcss/oxide-win32-x64-msvc@4.1.15':
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.16':
optional: true
- '@tailwindcss/oxide@4.1.15':
+ '@tailwindcss/oxide@4.1.16':
optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.1.15
- '@tailwindcss/oxide-darwin-arm64': 4.1.15
- '@tailwindcss/oxide-darwin-x64': 4.1.15
- '@tailwindcss/oxide-freebsd-x64': 4.1.15
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.15
- '@tailwindcss/oxide-linux-arm64-gnu': 4.1.15
- '@tailwindcss/oxide-linux-arm64-musl': 4.1.15
- '@tailwindcss/oxide-linux-x64-gnu': 4.1.15
- '@tailwindcss/oxide-linux-x64-musl': 4.1.15
- '@tailwindcss/oxide-wasm32-wasi': 4.1.15
- '@tailwindcss/oxide-win32-arm64-msvc': 4.1.15
- '@tailwindcss/oxide-win32-x64-msvc': 4.1.15
-
- '@tailwindcss/typography@0.5.19(tailwindcss@4.1.15)':
+ '@tailwindcss/oxide-android-arm64': 4.1.16
+ '@tailwindcss/oxide-darwin-arm64': 4.1.16
+ '@tailwindcss/oxide-darwin-x64': 4.1.16
+ '@tailwindcss/oxide-freebsd-x64': 4.1.16
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.16
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.16
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.16
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.16
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.16
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.16
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.16
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.16
+
+ '@tailwindcss/typography@0.5.19(tailwindcss@4.1.16)':
dependencies:
postcss-selector-parser: 6.0.10
- tailwindcss: 4.1.15
+ tailwindcss: 4.1.16
- '@tailwindcss/vite@4.1.15(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@tailwindcss/vite@4.1.16(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@tailwindcss/node': 4.1.15
- '@tailwindcss/oxide': 4.1.15
- tailwindcss: 4.1.15
- vite: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ '@tailwindcss/node': 4.1.16
+ '@tailwindcss/oxide': 4.1.16
+ tailwindcss: 4.1.16
+ vite: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
- '@tailwindcss/vite@4.1.15(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@tailwindcss/vite@4.1.16(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@tailwindcss/node': 4.1.15
- '@tailwindcss/oxide': 4.1.15
- tailwindcss: 4.1.15
- vite: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ '@tailwindcss/node': 4.1.16
+ '@tailwindcss/oxide': 4.1.16
+ tailwindcss: 4.1.16
+ vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
'@tsconfig/svelte@5.0.5': {}
'@types/babel__core@7.20.5':
dependencies:
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
'@types/babel__generator': 7.27.0
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.28.0
'@types/babel__generator@7.27.0':
dependencies:
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
'@types/babel__template@7.4.4':
dependencies:
- '@babel/parser': 7.28.4
- '@babel/types': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
'@types/babel__traverse@7.28.0':
dependencies:
- '@babel/types': 7.28.4
+ '@babel/types': 7.28.5
'@types/cookie@0.6.0': {}
@@ -4733,11 +4748,11 @@ snapshots:
dependencies:
undici-types: 5.26.5
- '@types/node@22.18.12':
+ '@types/node@22.19.0':
dependencies:
undici-types: 6.21.0
- '@types/node@24.9.1':
+ '@types/node@24.10.0':
dependencies:
undici-types: 7.16.0
@@ -4755,15 +4770,15 @@ snapshots:
'@types/triple-beam@1.3.5': {}
- '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
- '@eslint-community/regexpp': 4.12.1
- '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/scope-manager': 8.46.2
- '@typescript-eslint/type-utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.46.2
- eslint: 9.38.0(jiti@2.6.1)
+ '@eslint-community/regexpp': 4.12.2
+ '@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.46.3
+ '@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.46.3
+ eslint: 9.39.1(jiti@2.6.1)
graphemer: 1.4.0
ignore: 7.0.5
natural-compare: 1.4.0
@@ -4772,56 +4787,56 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/scope-manager': 8.46.2
- '@typescript-eslint/types': 8.46.2
- '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.46.2
+ '@typescript-eslint/scope-manager': 8.46.3
+ '@typescript-eslint/types': 8.46.3
+ '@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.46.3
debug: 4.4.3(supports-color@5.5.0)
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)':
+ '@typescript-eslint/project-service@8.46.3(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
- '@typescript-eslint/types': 8.46.2
+ '@typescript-eslint/tsconfig-utils': 8.46.3(typescript@5.9.3)
+ '@typescript-eslint/types': 8.46.3
debug: 4.4.3(supports-color@5.5.0)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.46.2':
+ '@typescript-eslint/scope-manager@8.46.3':
dependencies:
- '@typescript-eslint/types': 8.46.2
- '@typescript-eslint/visitor-keys': 8.46.2
+ '@typescript-eslint/types': 8.46.3
+ '@typescript-eslint/visitor-keys': 8.46.3
- '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)':
+ '@typescript-eslint/tsconfig-utils@8.46.3(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
- '@typescript-eslint/type-utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/type-utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/types': 8.46.2
- '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
- '@typescript-eslint/utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/types': 8.46.3
+ '@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
debug: 4.4.3(supports-color@5.5.0)
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@8.46.2': {}
+ '@typescript-eslint/types@8.46.3': {}
- '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)':
+ '@typescript-eslint/typescript-estree@8.46.3(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3)
- '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
- '@typescript-eslint/types': 8.46.2
- '@typescript-eslint/visitor-keys': 8.46.2
+ '@typescript-eslint/project-service': 8.46.3(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.46.3(typescript@5.9.3)
+ '@typescript-eslint/types': 8.46.3
+ '@typescript-eslint/visitor-keys': 8.46.3
debug: 4.4.3(supports-color@5.5.0)
fast-glob: 3.3.3
is-glob: 4.0.3
@@ -4832,37 +4847,37 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1))
- '@typescript-eslint/scope-manager': 8.46.2
- '@typescript-eslint/types': 8.46.2
- '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
- eslint: 9.38.0(jiti@2.6.1)
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
+ '@typescript-eslint/scope-manager': 8.46.3
+ '@typescript-eslint/types': 8.46.3
+ '@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
+ eslint: 9.39.1(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@8.46.2':
+ '@typescript-eslint/visitor-keys@8.46.3':
dependencies:
- '@typescript-eslint/types': 8.46.2
+ '@typescript-eslint/types': 8.46.3
eslint-visitor-keys: 4.2.1
- '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
+ '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))':
dependencies:
- '@babel/core': 7.28.4
- '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
- '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4)
+ '@babel/core': 7.28.5
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5)
'@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
- vite: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))':
+ '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0))(vue@3.5.22(typescript@5.9.3))':
dependencies:
- vite: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vue: 3.5.22(typescript@5.9.3)
'@volar/language-core@2.4.15':
@@ -4879,7 +4894,7 @@ snapshots:
'@vue/compiler-core@3.5.22':
dependencies:
- '@babel/parser': 7.28.4
+ '@babel/parser': 7.28.5
'@vue/shared': 3.5.22
entities: 4.5.0
estree-walker: 2.0.2
@@ -4892,13 +4907,13 @@ snapshots:
'@vue/compiler-sfc@3.5.22':
dependencies:
- '@babel/parser': 7.28.4
+ '@babel/parser': 7.28.5
'@vue/compiler-core': 3.5.22
'@vue/compiler-dom': 3.5.22
'@vue/compiler-ssr': 3.5.22
'@vue/shared': 3.5.22
estree-walker: 2.0.2
- magic-string: 0.30.19
+ magic-string: 0.30.21
postcss: 8.5.6
source-map-js: 1.2.1
@@ -4912,14 +4927,14 @@ snapshots:
de-indent: 1.0.2
he: 1.2.0
- '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@2.6.1))))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)':
+ '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1))))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- eslint: 9.38.0(jiti@2.6.1)
- eslint-plugin-vue: 10.5.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@2.6.1)))
+ '@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 9.39.1(jiti@2.6.1)
+ eslint-plugin-vue: 10.5.1(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)))
fast-glob: 3.3.3
- typescript-eslint: 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- vue-eslint-parser: 10.2.0(eslint@9.38.0(jiti@2.6.1))
+ typescript-eslint: 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@2.6.1))
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
@@ -5196,7 +5211,7 @@ snapshots:
dependencies:
possible-typed-array-names: 1.1.0
- axios@1.12.2:
+ axios@1.13.1:
dependencies:
follow-redirects: 1.15.11
form-data: 4.0.4
@@ -5208,7 +5223,7 @@ snapshots:
balanced-match@1.0.2: {}
- baseline-browser-mapping@2.8.18: {}
+ baseline-browser-mapping@2.8.23: {}
big.js@5.2.2: {}
@@ -5246,13 +5261,13 @@ snapshots:
dependencies:
fill-range: 7.1.1
- browserslist@4.26.3:
+ browserslist@4.27.0:
dependencies:
- baseline-browser-mapping: 2.8.18
- caniuse-lite: 1.0.30001751
- electron-to-chromium: 1.5.237
- node-releases: 2.0.26
- update-browserslist-db: 1.1.3(browserslist@4.26.3)
+ baseline-browser-mapping: 2.8.23
+ caniuse-lite: 1.0.30001753
+ electron-to-chromium: 1.5.244
+ node-releases: 2.0.27
+ update-browserslist-db: 1.1.4(browserslist@4.27.0)
buffer-from@1.1.2: {}
@@ -5281,7 +5296,7 @@ snapshots:
callsites@3.1.0: {}
- caniuse-lite@1.0.30001751: {}
+ caniuse-lite@1.0.30001753: {}
chalk@2.4.2:
dependencies:
@@ -5465,7 +5480,7 @@ snapshots:
detect-libc@2.1.2: {}
- devalue@5.4.1: {}
+ devalue@5.4.2: {}
doctrine@2.1.0:
dependencies:
@@ -5483,7 +5498,7 @@ snapshots:
ee-first@1.1.1: {}
- electron-to-chromium@1.5.237: {}
+ electron-to-chromium@1.5.244: {}
emoji-regex@8.0.0: {}
@@ -5568,7 +5583,7 @@ snapshots:
dependencies:
acorn: 8.15.0
acorn-walk: 8.3.4
- browserslist: 4.26.3
+ browserslist: 4.27.0
commander: 14.0.1
fast-brake: 0.1.6
fast-glob: 3.3.3
@@ -5623,9 +5638,9 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
- esbuild-node-externals@1.18.0(esbuild@0.25.11):
+ esbuild-node-externals@1.18.0(esbuild@0.25.12):
dependencies:
- esbuild: 0.25.11
+ esbuild: 0.25.12
find-up: 5.0.0
esbuild@0.21.5:
@@ -5654,34 +5669,34 @@ snapshots:
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
- esbuild@0.25.11:
+ esbuild@0.25.12:
optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.11
- '@esbuild/android-arm': 0.25.11
- '@esbuild/android-arm64': 0.25.11
- '@esbuild/android-x64': 0.25.11
- '@esbuild/darwin-arm64': 0.25.11
- '@esbuild/darwin-x64': 0.25.11
- '@esbuild/freebsd-arm64': 0.25.11
- '@esbuild/freebsd-x64': 0.25.11
- '@esbuild/linux-arm': 0.25.11
- '@esbuild/linux-arm64': 0.25.11
- '@esbuild/linux-ia32': 0.25.11
- '@esbuild/linux-loong64': 0.25.11
- '@esbuild/linux-mips64el': 0.25.11
- '@esbuild/linux-ppc64': 0.25.11
- '@esbuild/linux-riscv64': 0.25.11
- '@esbuild/linux-s390x': 0.25.11
- '@esbuild/linux-x64': 0.25.11
- '@esbuild/netbsd-arm64': 0.25.11
- '@esbuild/netbsd-x64': 0.25.11
- '@esbuild/openbsd-arm64': 0.25.11
- '@esbuild/openbsd-x64': 0.25.11
- '@esbuild/openharmony-arm64': 0.25.11
- '@esbuild/sunos-x64': 0.25.11
- '@esbuild/win32-arm64': 0.25.11
- '@esbuild/win32-ia32': 0.25.11
- '@esbuild/win32-x64': 0.25.11
+ '@esbuild/aix-ppc64': 0.25.12
+ '@esbuild/android-arm': 0.25.12
+ '@esbuild/android-arm64': 0.25.12
+ '@esbuild/android-x64': 0.25.12
+ '@esbuild/darwin-arm64': 0.25.12
+ '@esbuild/darwin-x64': 0.25.12
+ '@esbuild/freebsd-arm64': 0.25.12
+ '@esbuild/freebsd-x64': 0.25.12
+ '@esbuild/linux-arm': 0.25.12
+ '@esbuild/linux-arm64': 0.25.12
+ '@esbuild/linux-ia32': 0.25.12
+ '@esbuild/linux-loong64': 0.25.12
+ '@esbuild/linux-mips64el': 0.25.12
+ '@esbuild/linux-ppc64': 0.25.12
+ '@esbuild/linux-riscv64': 0.25.12
+ '@esbuild/linux-s390x': 0.25.12
+ '@esbuild/linux-x64': 0.25.12
+ '@esbuild/netbsd-arm64': 0.25.12
+ '@esbuild/netbsd-x64': 0.25.12
+ '@esbuild/openbsd-arm64': 0.25.12
+ '@esbuild/openbsd-x64': 0.25.12
+ '@esbuild/openharmony-arm64': 0.25.12
+ '@esbuild/sunos-x64': 0.25.12
+ '@esbuild/win32-arm64': 0.25.12
+ '@esbuild/win32-ia32': 0.25.12
+ '@esbuild/win32-x64': 0.25.12
escalade@3.2.0: {}
@@ -5691,29 +5706,29 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-compat-utils@0.5.1(eslint@9.38.0(jiti@2.6.1)):
+ eslint-compat-utils@0.5.1(eslint@9.39.1(jiti@2.6.1)):
dependencies:
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
semver: 7.7.3
- eslint-config-prettier@10.1.8(eslint@9.38.0(jiti@2.6.1)):
+ eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)):
dependencies:
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
- eslint-plugin-es-x@7.8.0(eslint@9.38.0(jiti@2.6.1)):
+ eslint-plugin-es-x@7.8.0(eslint@9.39.1(jiti@2.6.1)):
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1))
- '@eslint-community/regexpp': 4.12.1
- eslint: 9.38.0(jiti@2.6.1)
- eslint-compat-utils: 0.5.1(eslint@9.38.0(jiti@2.6.1))
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
+ '@eslint-community/regexpp': 4.12.2
+ eslint: 9.39.1(jiti@2.6.1)
+ eslint-compat-utils: 0.5.1(eslint@9.39.1(jiti@2.6.1))
- eslint-plugin-n@17.23.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3):
+ eslint-plugin-n@17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1))
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
enhanced-resolve: 5.18.3
- eslint: 9.38.0(jiti@2.6.1)
- eslint-plugin-es-x: 7.8.0(eslint@9.38.0(jiti@2.6.1))
- get-tsconfig: 4.12.0
+ eslint: 9.39.1(jiti@2.6.1)
+ eslint-plugin-es-x: 7.8.0(eslint@9.39.1(jiti@2.6.1))
+ get-tsconfig: 4.13.0
globals: 15.15.0
globrex: 0.1.2
ignore: 5.3.2
@@ -5722,11 +5737,11 @@ snapshots:
transitivePeerDependencies:
- typescript
- eslint-plugin-react-hooks@5.2.0(eslint@9.38.0(jiti@2.6.1)):
+ eslint-plugin-react-hooks@5.2.0(eslint@9.39.1(jiti@2.6.1)):
dependencies:
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
- eslint-plugin-react@7.37.5(eslint@9.38.0(jiti@2.6.1)):
+ eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)):
dependencies:
array-includes: 3.1.9
array.prototype.findlast: 1.2.5
@@ -5734,7 +5749,7 @@ snapshots:
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.2.1
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -5748,13 +5763,13 @@ snapshots:
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
- eslint-plugin-svelte@3.12.5(eslint@9.38.0(jiti@2.6.1))(svelte@4.2.20):
+ eslint-plugin-svelte@3.13.0(eslint@9.39.1(jiti@2.6.1))(svelte@4.2.20):
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1))
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
'@jridgewell/sourcemap-codec': 1.5.5
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
esutils: 2.0.3
- globals: 16.4.0
+ globals: 16.5.0
known-css-properties: 0.37.0
postcss: 8.5.6
postcss-load-config: 3.1.4(postcss@8.5.6)
@@ -5766,18 +5781,18 @@ snapshots:
transitivePeerDependencies:
- ts-node
- eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@2.6.1))):
+ eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1))):
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1))
- eslint: 9.38.0(jiti@2.6.1)
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
+ eslint: 9.39.1(jiti@2.6.1)
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 6.1.2
semver: 7.7.3
- vue-eslint-parser: 10.2.0(eslint@9.38.0(jiti@2.6.1))
+ vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@2.6.1))
xml-name-validator: 4.0.0
optionalDependencies:
- '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
eslint-scope@5.1.1:
dependencies:
@@ -5846,16 +5861,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint@9.38.0(jiti@2.6.1):
+ eslint@9.39.1(jiti@2.6.1):
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1))
- '@eslint-community/regexpp': 4.12.1
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
+ '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.1
- '@eslint/config-helpers': 0.4.1
- '@eslint/core': 0.16.0
+ '@eslint/config-helpers': 0.4.2
+ '@eslint/core': 0.17.0
'@eslint/eslintrc': 3.3.1
- '@eslint/js': 9.38.0
- '@eslint/plugin-kit': 0.4.0
+ '@eslint/js': 9.39.1
+ '@eslint/plugin-kit': 0.4.1
'@humanfs/node': 0.16.7
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.3
@@ -5907,7 +5922,7 @@ snapshots:
dependencies:
estraverse: 5.3.0
- esrap@2.1.0:
+ esrap@2.1.2:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -6108,7 +6123,7 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.3.0
- get-tsconfig@4.12.0:
+ get-tsconfig@4.13.0:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -6147,7 +6162,7 @@ snapshots:
globals@15.15.0: {}
- globals@16.4.0: {}
+ globals@16.5.0: {}
globalthis@1.0.4:
dependencies:
@@ -6375,7 +6390,7 @@ snapshots:
jest-worker@27.5.1:
dependencies:
- '@types/node': 24.9.1
+ '@types/node': 24.10.0
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -6423,16 +6438,23 @@ snapshots:
kuler@2.0.0: {}
- laravel-vite-plugin@1.3.0(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)):
+ laravel-precognition@0.7.3:
+ dependencies:
+ axios: 1.13.1
+ lodash-es: 4.17.21
+ transitivePeerDependencies:
+ - debug
+
+ laravel-vite-plugin@1.3.0(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)):
dependencies:
picocolors: 1.1.1
- vite: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
vite-plugin-full-reload: 1.2.0
- laravel-vite-plugin@1.3.0(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)):
+ laravel-vite-plugin@1.3.0(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)):
dependencies:
picocolors: 1.1.1
- vite: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vite-plugin-full-reload: 1.2.0
levn@0.4.1:
@@ -6532,7 +6554,7 @@ snapshots:
dependencies:
yallist: 3.1.1
- magic-string@0.30.19:
+ magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -6613,7 +6635,7 @@ snapshots:
neo-async@2.6.2: {}
- node-releases@2.0.26: {}
+ node-releases@2.0.27: {}
nodemon@3.1.10:
dependencies:
@@ -6826,17 +6848,17 @@ snapshots:
optionalDependencies:
vue-tsc: 2.2.12(typescript@5.9.3)
- prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.41.1):
+ prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.3):
dependencies:
prettier: 3.6.2
- svelte: 5.41.1
+ svelte: 5.43.3
- prettier-plugin-tailwindcss@0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.41.1))(prettier@3.6.2):
+ prettier-plugin-tailwindcss@0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3)))(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.3))(prettier@3.6.2):
dependencies:
prettier: 3.6.2
optionalDependencies:
prettier-plugin-organize-imports: 4.3.0(prettier@3.6.2)(typescript@5.9.3)(vue-tsc@2.2.12(typescript@5.9.3))
- prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.41.1)
+ prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.43.3)
prettier@3.6.2: {}
@@ -7064,7 +7086,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- set-cookie-parser@2.7.1: {}
+ set-cookie-parser@2.7.2: {}
set-function-length@1.2.2:
dependencies:
@@ -7250,14 +7272,14 @@ snapshots:
transitivePeerDependencies:
- picomatch
- svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.41.1)(typescript@5.9.3):
+ svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.43.3)(typescript@5.9.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
chokidar: 4.0.3
fdir: 6.5.0(picomatch@4.0.3)
picocolors: 1.1.1
sade: 1.8.1
- svelte: 5.41.1
+ svelte: 5.43.3
typescript: 5.9.3
transitivePeerDependencies:
- picomatch
@@ -7298,10 +7320,10 @@ snapshots:
estree-walker: 3.0.3
is-reference: 3.0.3
locate-character: 3.0.0
- magic-string: 0.30.19
+ magic-string: 0.30.21
periscopic: 3.1.0
- svelte@5.41.1:
+ svelte@5.43.3:
dependencies:
'@jridgewell/remapping': 2.3.5
'@jridgewell/sourcemap-codec': 1.5.5
@@ -7312,10 +7334,10 @@ snapshots:
axobject-query: 4.1.0
clsx: 2.1.1
esm-env: 1.2.2
- esrap: 2.1.0
+ esrap: 2.1.2
is-reference: 3.0.3
locate-character: 3.0.0
- magic-string: 0.30.19
+ magic-string: 0.30.21
zimmerframe: 1.1.4
table@6.9.0:
@@ -7326,7 +7348,7 @@ snapshots:
string-width: 4.2.3
strip-ansi: 6.0.1
- tailwindcss@4.1.15: {}
+ tailwindcss@4.1.16: {}
tapable@2.3.0: {}
@@ -7424,13 +7446,13 @@ snapshots:
typedarray@0.0.6: {}
- typescript-eslint@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3):
+ typescript-eslint@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
dependencies:
- '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
- '@typescript-eslint/utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
- eslint: 9.38.0(jiti@2.6.1)
+ '@typescript-eslint/eslint-plugin': 8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 9.39.1(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -7456,9 +7478,9 @@ snapshots:
unpipe@1.0.0: {}
- update-browserslist-db@1.1.3(browserslist@4.26.3):
+ update-browserslist-db@1.1.4(browserslist@4.27.0):
dependencies:
- browserslist: 4.26.3
+ browserslist: 4.27.0
escalade: 3.2.0
picocolors: 1.1.1
@@ -7479,46 +7501,46 @@ snapshots:
picocolors: 1.1.1
picomatch: 2.3.1
- vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0):
+ vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0):
dependencies:
esbuild: 0.21.5
postcss: 8.5.6
rollup: 4.52.5
optionalDependencies:
- '@types/node': 24.9.1
+ '@types/node': 24.10.0
fsevents: 2.3.3
lightningcss: 1.30.2
terser: 5.44.0
- vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0):
+ vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0):
dependencies:
- esbuild: 0.25.11
+ esbuild: 0.25.12
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.9.1
+ '@types/node': 24.10.0
fsevents: 2.3.3
jiti: 2.6.1
lightningcss: 1.30.2
terser: 5.44.0
- vitefu@0.2.5(vite@5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)):
+ vitefu@0.2.5(vite@5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)):
optionalDependencies:
- vite: 5.4.21(@types/node@24.9.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 5.4.21(@types/node@24.10.0)(lightningcss@1.30.2)(terser@5.44.0)
- vitefu@1.1.1(vite@6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)):
+ vitefu@1.1.1(vite@6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)):
optionalDependencies:
- vite: 6.4.1(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
+ vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)
vscode-uri@3.1.0: {}
- vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@2.6.1)):
+ vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
- eslint: 9.38.0(jiti@2.6.1)
+ eslint: 9.39.1(jiti@2.6.1)
eslint-scope: 8.4.0
eslint-visitor-keys: 4.2.1
espree: 10.4.0
@@ -7560,7 +7582,7 @@ snapshots:
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.15.0
acorn-import-phases: 1.0.4(acorn@8.15.0)
- browserslist: 4.26.3
+ browserslist: 4.27.0
chrome-trace-event: 1.0.4
enhanced-resolve: 5.18.3
es-module-lexer: 1.7.0
diff --git a/tests/app/server.js b/tests/app/server.js
index f9238a104..d011d8006 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -282,6 +282,158 @@ app.post('/form-helper/events/errors', (req, res) => {
}, 250)
})
+app.post('/precognition/default', (req, res) => {
+ setTimeout(
+ () => {
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const email = req.body['email']
+ const errors = {}
+
+ if (!name) {
+ errors.name = 'The name field is required.'
+ }
+
+ if (name && name.length < 3) {
+ errors.name = 'The name must be at least 3 characters.'
+ }
+
+ if (!email) {
+ errors.email = 'The email field is required.'
+ }
+
+ if (email && !/\S+@\S+\.\S+/.test(email)) {
+ errors.email = 'The email must be a valid email address.'
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ },
+ !!req.query['slow'] ? 2000 : 250,
+ )
+})
+
+app.post('/precognition/array-errors', (req, res) => {
+ setTimeout(() => {
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const email = req.body['email']
+ const errors = {}
+
+ if (!name) {
+ errors.name = ['The name field is required.']
+ }
+
+ if (name && name.length < 3) {
+ errors.name = ['The name must be at least 3 characters.', 'The name contains invalid characters.']
+ }
+
+ if (!email) {
+ errors.email = ['The email field is required.']
+ }
+
+ if (email && !/\S+@\S+\.\S+/.test(email)) {
+ errors.email = ['The email must be a valid email address.', 'The email format is incorrect.']
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 250)
+})
+
+app.post('/precognition/files', upload.any(), (req, res) => {
+ setTimeout(() => {
+ console.log(req, req)
+ const only = req.headers['precognition-validate-only'] ? req.headers['precognition-validate-only'].split(',') : []
+ const name = req.body['name']
+ const hasAvatar = req.files && req.files.avatar
+ const errors = {}
+
+ if (!name) {
+ errors.name = 'The name field is required.'
+ }
+
+ if (name && name.length < 3) {
+ errors.name = 'The name must be at least 3 characters.'
+ }
+
+ if (!hasAvatar) {
+ errors.avatar = 'The avatar field is required.'
+ }
+
+ if (only.length) {
+ Object.keys(errors).forEach((key) => {
+ if (!only.includes(key)) {
+ delete errors[key]
+ }
+ })
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 250)
+})
+
+app.post('/precognition/headers', (req, res) => {
+ setTimeout(() => {
+ const customHeader = req.headers['x-custom-header']
+ const name = req.body['name']
+ const errors = {}
+
+ // Show error when custom header IS present (to prove it was sent)
+ if (customHeader === 'custom-value') {
+ errors.name = 'Custom header received: custom-value'
+ } else if (!name) {
+ errors.name = 'The name field is required.'
+ } else if (name.length < 3) {
+ errors.name = 'The name must be at least 3 characters.'
+ }
+
+ res.header('Precognition', 'true')
+ res.header('Vary', 'Precognition')
+
+ if (Object.keys(errors).length) {
+ return res.status(422).json({ errors })
+ }
+
+ return res.status(204).header('Precognition-Success', 'true').send()
+ }, 250)
+})
+
const methods = ['get', 'post', 'put', 'patch', 'delete']
methods.forEach((method) =>
diff --git a/tests/precognition.spec.ts b/tests/precognition.spec.ts
new file mode 100644
index 000000000..9331e6c37
--- /dev/null
+++ b/tests/precognition.spec.ts
@@ -0,0 +1,538 @@
+import test, { expect } from '@playwright/test'
+import { requests } from './support'
+
+const integrations = ['form-helper']
+
+integrations.forEach((integration) => {
+ test.skip(process.env.PACKAGE !== 'vue', '[WIP] Only for Vue')
+
+ test.describe(integration === 'form-helper' ? 'Form Helper' : 'Form Component', () => {
+ test.describe('Precognition', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/default')
+ })
+
+ test('does not validate when field is untouched', async ({ page }) => {
+ await page.locator('input[name="name"]').focus()
+ await page.waitForTimeout(100)
+ await page.locator('input[name="name"]').blur()
+
+ for (let i = 0; i < 5; i++) {
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await page.waitForTimeout(50)
+ }
+
+ await expect(page.getByText('The name field is required.')).not.toBeVisible()
+ })
+
+ test('shows validation error when field is invalid', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ })
+
+ test('clears validation error when field becomes valid', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ })
+
+ test('validates only the specified field', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).not.toBeVisible()
+ })
+
+ test('validates multiple fields independently', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
+
+ test('does not clear unrelated field errors', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+
+ await page.fill('input[name="email"]', 'test@example.com')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ })
+
+ test('field is valid when validated and no errors exist', async ({ page }) => {
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ })
+
+ test('field is not valid before validation', async ({ page }) => {
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ })
+
+ test('field is not valid after failed validation', async ({ page }) => {
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+ })
+
+ test('valid field persists after successful validation', async ({ page }) => {
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'Jane Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ })
+
+ test('valid field becomes invalid when field is revalidated with errors', async ({ page }) => {
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('Name is valid!')).not.toBeVisible()
+ })
+
+ test('shows only first error when server returns errors as array', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/array-errors')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show only the first error from the array, not the second
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The name contains invalid characters.')).not.toBeVisible()
+ })
+
+ test('shows all errors when simpleValidationErrors is false', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/all-errors')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show all errors from the array
+ await expect(page.locator('#name-error-0')).toHaveText('The name must be at least 3 characters.')
+ await expect(page.locator('#name-error-1')).toHaveText('The name contains invalid characters.')
+ })
+
+ test('validates all touched fields when calling validate() without arguments', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
+
+ test('reset all fields clears all touched fields', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await page.getByRole('button', { name: 'Reset All' }).click()
+
+ await expect(page.locator('input[name="name"]')).toHaveValue('')
+ await expect(page.locator('input[name="email"]')).toHaveValue('')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).not.toBeVisible()
+ })
+
+ test('reset specific fields removes only those fields from touched', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.locator('input[name="name"]')).toHaveValue('ab')
+ await expect(page.locator('input[name="email"]')).toHaveValue('x')
+
+ await page.getByRole('button', { name: 'Reset Name', exact: true }).click()
+
+ await expect(page.locator('input[name="name"]')).toHaveValue('')
+ await expect(page.locator('input[name="email"]')).toHaveValue('x')
+
+ await page.fill('input[name="email"]', 'y')
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name field is required.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
+
+ test('touch with array marks multiple fields as touched', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.getByRole('button', { name: 'Touch Name and Email' }).click()
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name field is required.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).toBeVisible()
+ })
+
+ test('touch deduplicates fields when called multiple times', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.fill('input[name="name"]', 'ab')
+
+ await page.getByRole('button', { name: 'Touch Name Twice' }).click()
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ })
+
+ test('touched() returns false when no fields are touched', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await expect(page.locator('#any-touched')).toHaveText('Form has no touched fields')
+ await expect(page.locator('#name-touched')).toHaveText('Name is not touched')
+ await expect(page.locator('#email-touched')).toHaveText('Email is not touched')
+ })
+
+ test('touched(field) returns true when specific field is touched', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.locator('input[name="name"]').focus()
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.locator('#name-touched')).toHaveText('Name is touched')
+ await expect(page.locator('#email-touched')).toHaveText('Email is not touched')
+ await expect(page.locator('#any-touched')).toHaveText('Form has touched fields')
+ })
+
+ test('touched() returns true when any field is touched', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.locator('input[name="email"]').focus()
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.locator('#any-touched')).toHaveText('Form has touched fields')
+ await expect(page.locator('#email-touched')).toHaveText('Email is touched')
+ await expect(page.locator('#name-touched')).toHaveText('Name is not touched')
+ })
+
+ test('touched() updates when multiple fields are touched', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.locator('input[name="name"]').focus()
+ await page.locator('input[name="name"]').blur()
+ await page.locator('input[name="email"]').focus()
+ await page.locator('input[name="email"]').blur()
+
+ await expect(page.locator('#name-touched')).toHaveText('Name is touched')
+ await expect(page.locator('#email-touched')).toHaveText('Email is touched')
+ await expect(page.locator('#any-touched')).toHaveText('Form has touched fields')
+ })
+
+ test('validating a specific field also validates previously touched inputs', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.fill('input[name="email"]', 'x')
+
+ await page.getByRole('button', { name: 'Validate Name', exact: true }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+ })
+
+ test('validate with array of fields validates multiple fields', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.getByRole('button', { name: 'Validate Name and Email' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name field is required.')).toBeVisible()
+ await expect(page.getByText('The email field is required.')).toBeVisible()
+ })
+
+ test('reset with array removes multiple fields from touched', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/methods')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.locator('input[name="name"]').blur()
+
+ await page.fill('input[name="email"]', 'x')
+ await page.locator('input[name="email"]').blur()
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).toBeVisible()
+
+ await page.getByRole('button', { name: 'Reset Name and Email' }).click()
+
+ await expect(page.locator('input[name="name"]')).toHaveValue('')
+ await expect(page.locator('input[name="email"]')).toHaveValue('')
+
+ await expect(page.getByText('Name is not touched')).toBeVisible()
+ await expect(page.getByText('Email is not touched')).toBeVisible()
+ await expect(page.getByText('Form has no touched fields')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'abc')
+ await page.fill('input[name="email"]', 'test@example.com')
+
+ await page.getByRole('button', { name: 'Validate All Touched' }).click()
+
+ await page.waitForTimeout(500)
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('The email must be a valid email address.')).not.toBeVisible()
+ })
+
+ test('does not submit files by default', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/files')
+
+ await page.setInputFiles('#avatar', {
+ name: 'avatar.jpg',
+ mimeType: 'image/jpeg',
+ buffer: Buffer.from('fake image data'),
+ })
+
+ await page.getByRole('button', { name: 'Validate Both' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name field is required.')).toBeVisible()
+ await expect(page.getByText('The avatar field is required.')).toBeVisible()
+ })
+
+ test('validates files when validate-files prop is true', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/files')
+ await page.getByRole('button', { name: /Toggle Validate Files/ }).click()
+ await expect(page.getByText('Toggle Validate Files (enabled)')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.setInputFiles('#avatar', {
+ name: 'avatar.jpg',
+ mimeType: 'image/jpeg',
+ buffer: Buffer.from('fake image data'),
+ })
+
+ await page.getByRole('button', { name: 'Validate Both' }).click()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+ await expect(page.getByText('The avatar field is required.')).not.toBeVisible()
+ })
+
+ test('transforms data for validation requests', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/transform')
+
+ await page.fill('input[name="name"]', 'a')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
+ await page.fill('input[name="name"]', 'aa')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('The name must be at least 3 characters.')).not.toBeVisible()
+ await expect(page.getByText('Name is valid!')).toBeVisible()
+ })
+
+ test('calls onPrecognitionSuccess and onFinish callbacks when validation succeeds', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/callbacks')
+
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.click('button:has-text("Validate")')
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('onPrecognitionSuccess called!')).toBeVisible()
+ await expect(page.getByText('onValidationError called!')).not.toBeVisible()
+ await expect(page.getByText('onFinish called!')).toBeVisible()
+ })
+
+ test('calls onValidationError and onFinish callbacks when validation fails', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/callbacks')
+
+ await page.fill('input[name="name"]', 'ab')
+ await page.click('button:has-text("Validate")')
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ await expect(page.getByText('onPrecognitionSuccess called!')).not.toBeVisible()
+ await expect(page.getByText('onValidationError called!')).toBeVisible()
+ await expect(page.getByText('onFinish called!')).toBeVisible()
+ })
+
+ test('onBefore can block validation', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/before-validation')
+
+ await page.fill('input[name="name"]', 'block')
+ await page.locator('input[name="name"]').blur()
+
+ for (let i = 0; i < 5; i++) {
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await page.waitForTimeout(50)
+ }
+ })
+
+ test('sends custom headers with validation requests', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/headers')
+
+ // Fill in a valid name to trigger validation
+ await page.fill('input[name="name"]', 'John Doe')
+ await page.locator('input[name="name"]').blur()
+
+ await expect(page.getByText('Validating...')).toBeVisible()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+
+ // Should show error confirming custom header was received
+ await expect(page.getByText('Custom header received: custom-value')).toBeVisible()
+ })
+
+ test('automatically cancels previous validation when new validation starts', async ({ page }) => {
+ await page.goto('/' + integration + '/precognition/cancel')
+
+ requests.listenForFailed(page)
+ requests.listenForResponses(page)
+
+ await page.fill('#auto-cancel-name-input', 'ab')
+ await page.locator('#auto-cancel-name-input').blur()
+ await expect(page.getByText('Validating...')).toBeVisible()
+
+ // Immediately change value and trigger new validation - should cancel the first one
+ await page.fill('#auto-cancel-name-input', 'xy')
+ await page.locator('#auto-cancel-name-input').blur()
+ await expect(page.getByText('Validating...')).not.toBeVisible()
+ await expect(page.getByText('The name must be at least 3 characters.')).toBeVisible()
+
+ // One cancelled, one 422 response
+ expect(requests.failed).toHaveLength(1)
+ expect(requests.responses).toHaveLength(1)
+
+ const cancelledRequestError = await requests.failed[0].failure()?.errorText
+ expect(cancelledRequestError).toBe('net::ERR_ABORTED')
+ })
+ })
+ })
+})
diff --git a/tests/support.ts b/tests/support.ts
index bae7d4abb..baba83c2b 100644
--- a/tests/support.ts
+++ b/tests/support.ts
@@ -43,6 +43,8 @@ export const consoleMessages = {
export const requests = {
requests: [] as Request[],
finished: [] as Request[],
+ failed: [] as Request[],
+ responses: [] as Response[],
listen(page: Page) {
this.requests = []
@@ -53,6 +55,16 @@ export const requests = {
this.finished = []
page.on('requestfinished', (request) => this.finished.push(request))
},
+
+ listenForFailed(page: Page) {
+ this.failed = []
+ page.on('requestfailed', (request) => this.failed.push(request))
+ },
+
+ listenForResponses(page: Page) {
+ this.responses = []
+ page.on('response', (data) => this.responses.push(data))
+ },
}
export const shouldBeDumpPage = async (page: Page, method: 'get' | 'post' | 'patch' | 'put' | 'delete') => {
From 350b6e4f28f3c0c49547fa28402455e835489d4a Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 4 Nov 2025 14:32:36 +0100
Subject: [PATCH 077/145] Update Files.vue
---
.../test-app/Pages/FormHelper/Precognition/Files.vue | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
index 9883699cd..761315d32 100644
--- a/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
@@ -2,7 +2,10 @@
import { useForm } from '@inertiajs/vue3'
import { ref, watch } from 'vue'
-const form = useForm({
+const form = useForm<{
+ name: string
+ avatar: File | null
+}>({
name: '',
avatar: null,
})
@@ -28,7 +31,12 @@ watch(validateFiles, (enabled) => {
-
(form.avatar = e.target.files[0])" />
+
(form.avatar = (e.target as HTMLInputElement).files?.[0] || null)"
+ />
{{ form.errors.avatar }}
From 065ff960e6ffdc714e54e5efb56b80826d0c8e2a Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 4 Nov 2025 14:38:21 +0100
Subject: [PATCH 078/145] Update Files.vue
---
.../vue3/test-app/Pages/FormHelper/Precognition/Files.vue | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
index 761315d32..c4566ed16 100644
--- a/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Files.vue
@@ -14,10 +14,7 @@ const form = useForm<{
const validateFiles = ref(false)
-watch(validateFiles, (enabled) => {
- enabled ? form.validateFiles() : form.withoutFileValidation()
- console.log(`File validation ${enabled ? 'enabled' : 'disabled'}`)
-})
+watch(validateFiles, (enabled) => (enabled ? form.validateFiles() : form.withoutFileValidation()))
From 9d830e7a7e912b58449b69acdb3eced0e0a386e4 Mon Sep 17 00:00:00 2001
From: Pascal Baljet
Date: Tue, 4 Nov 2025 23:48:24 +0100
Subject: [PATCH 079/145] Update useForm.ts
---
packages/vue3/src/useForm.backup.ts | 600 ++++++++++++++++++
packages/vue3/src/useForm.ts | 144 +++--
.../FormHelper/Precognition/Instantiate.vue | 77 +++
.../FormHelper/TypeScript/Precognition.vue | 40 ++
tests/app/server.js | 34 +-
tests/precognition.spec.ts | 83 ++-
6 files changed, 906 insertions(+), 72 deletions(-)
create mode 100644 packages/vue3/src/useForm.backup.ts
create mode 100644 packages/vue3/test-app/Pages/FormHelper/Precognition/Instantiate.vue
create mode 100644 packages/vue3/test-app/Pages/FormHelper/TypeScript/Precognition.vue
diff --git a/packages/vue3/src/useForm.backup.ts b/packages/vue3/src/useForm.backup.ts
new file mode 100644
index 000000000..9ec324e4b
--- /dev/null
+++ b/packages/vue3/src/useForm.backup.ts
@@ -0,0 +1,600 @@
+import {
+ CancelToken,
+ Errors,
+ ErrorValue,
+ FormDataErrors,
+ FormDataKeys,
+ FormDataType,
+ FormDataValues,
+ isUrlMethodPair,
+ Method,
+ Progress,
+ RequestPayload,
+ router,
+ UrlMethodPair,
+ VisitOptions,
+} from '@inertiajs/core'
+import { AxiosProgressEvent } from 'axios'
+import {
+ createValidator,
+ NamedInputEvent,
+ resolveName,
+ toSimpleValidationErrors,
+ ValidationConfig,
+ Validator,
+} from 'laravel-precognition'
+import { cloneDeep, get, has, isEqual, set } from 'lodash-es'
+import { reactive, watch } from 'vue'
+import { config } from '.'
+
+type FormOptions = Omit
+type SubmitArgs = [Method, string, FormOptions?] | [UrlMethodPair, FormOptions?] | [FormOptions?]
+type TransformCallback = (data: TForm) => object
+type PrecognitionValidationConfig = ValidationConfig & {
+ only?: FormDataKeys[]
+}
+
+type UseFormArgs =
+ | [data: TForm | (() => TForm)] // useForm({})
+ | [rememberKey: string, data: TForm | (() => TForm)] // useForm('key', {})
+ | [method: Method | (() => Method), url: string | (() => string), data: TForm | (() => TForm)] // useForm('post', '/url', {})
+ | [urlMethodPair: UrlMethodPair | (() => UrlMethodPair), data: TForm | (() => TForm)] // useForm({ method: 'post', url: '/url' }, {})
+
+type ParsedUseFormArgs = {
+ rememberKey: string | null
+ data: TForm | (() => TForm)
+ resolvePrecognitionEndpoint: (() => UrlMethodPair) | null
+}
+
+function parseUseFormArgs>(args: UseFormArgs): ParsedUseFormArgs {
+ const parsed = {
+ rememberKey: null,
+ data: {},
+ resolvePrecognitionEndpoint: null,
+ }
+
+ if (args.length === 1) {
+ return {
+ ...parsed,
+ data: args[0],
+ }
+ }
+
+ if (args.length === 2) {
+ if (typeof args[0] === 'string') {
+ // useForm(rememberKey, data)
+ return {
+ ...parsed,
+ rememberKey: args[0],
+ data: args[1],
+ }
+ }
+
+ // useForm(urlMethodPair, data)
+ return {
+ ...parsed,
+ data: args[1],
+ resolvePrecognitionEndpoint: () => {
+ if (typeof args[0] === 'function') {
+ return args[0]()
+ }
+
+ if (typeof args[0] === 'object') {
+ return args[0]
+ }
+
+ throw new Error('Invalid argument for urlMethodPair')
+ },
+ }
+ }
+
+ // useForm(method, url, data)
+ return {
+ ...parsed,
+ data: args[2],
+ resolvePrecognitionEndpoint: () => {
+ const method = typeof args[0] === 'function' ? args[0]() : args[0]
+ const url = typeof args[1] === 'function' ? args[1]() : args[1]
+
+ return { method, url }
+ },
+ }
+}
+
+export interface InertiaFormProps {
+ isDirty: boolean
+ errors: FormDataErrors
+ hasErrors: boolean
+ processing: boolean
+ progress: Progress | null
+ wasSuccessful: boolean
+ recentlySuccessful: boolean
+ data(): TForm
+ transform(callback: TransformCallback): this
+ defaults(): this
+ defaults>(field: T, value: FormDataValues): this
+ defaults(fields: Partial): this
+ reset>(...fields: K[]): this
+ clearErrors>(...fields: K[]): this
+ resetAndClearErrors>(...fields: K[]): this
+ setError>(field: K, value: ErrorValue): this
+ setError(errors: FormDataErrors): this
+ submit: (...args: SubmitArgs) => void
+ get(url: string, options?: FormOptions): void
+ post(url: string, options?: FormOptions): void
+ put(url: string, options?: FormOptions): void
+ patch(url: string, options?: FormOptions): void
+ delete(url: string, options?: FormOptions): void
+ cancel(): void
+ withPrecognition(
+ method: Method | UrlMethodPair | (() => Method) | (() => UrlMethodPair),
+ url?: string | (() => string),
+ ): InertiaPrecognitiveForm
+}
+
+export interface InertiaFormValidationProps {
+ validating: boolean
+ valid>(field: K): boolean
+ invalid>(field: K): boolean
+ touched>(field?: K): boolean
+ validate>(
+ field?: K | NamedInputEvent | PrecognitionValidationConfig,
+ config?: PrecognitionValidationConfig,
+ ): this
+ touch>(...fields: K[]): this
+ setValidationTimeout(duration: number): this
+ validateFiles(): this
+ validator: () => Validator
+ withFullErrors(): this
+ withoutFileValidation(): this
+}
+
+export type InertiaForm = TForm & InertiaFormProps
+export type InertiaPrecognitiveForm = InertiaForm & InertiaFormValidationProps
+
+type InternalFormProps = {
+ __rememberable: boolean
+ __remember(): { data: TForm; errors: FormDataErrors }
+ __restore(restored: { data: TForm; errors: FormDataErrors }): void
+}
+
+type InternalValidationProps = {
+ __touched: string[]
+ __valid: string[]
+}
+
+export default function useForm>(
+ method: Method | (() => Method),
+ url: string | (() => string),
+ data: TForm | (() => TForm),
+): InertiaPrecognitiveForm
+export default function useForm>(
+ urlMethodPair: UrlMethodPair | (() => UrlMethodPair),
+ data: TForm | (() => TForm),
+): InertiaPrecognitiveForm
+export default function useForm>(data: TForm | (() => TForm)): InertiaForm
+export default function useForm>(
+ rememberKey: string,
+ data: TForm | (() => TForm),
+): InertiaForm
+export default function useForm>(...args: UseFormArgs) {
+ let { rememberKey, data, resolvePrecognitionEndpoint } = parseUseFormArgs(args)
+
+ const restored = rememberKey
+ ? (router.restore(rememberKey) as { data: TForm; errors: Record, string> })
+ : null
+ let defaults = typeof data === 'function' ? cloneDeep(data()) : cloneDeep(data)
+ let cancelToken: CancelToken | null = null
+ let recentlySuccessfulTimeoutId: ReturnType
+ let transform: TransformCallback = (data) => data
+
+ let validator: Validator | null = null
+
+ const parseSubmitArgs = (args: SubmitArgs): [Method, string, FormOptions] => {
+ let method: Method
+ let url: string
+
+ if (resolvePrecognitionEndpoint) {
+ // No arguments passed, but Precognition enabled, use those values as defaults...
+ const urlMethod = resolvePrecognitionEndpoint()
+ method = urlMethod.method
+ url = urlMethod.url
+ }
+
+ let options: FormOptions = {}
+
+ if (args.length === 0) {
+ // No arguments passed, use precognition values
+ return [method!, url!, options]
+ }
+
+ if (args.length === 3) {
+ // All arguments passed
+ return [args[0], args[1], args[2] ?? {}]
+ }
+
+ if (isUrlMethodPair(args[0])) {
+ // Wayfinder + optional options
+ return [args[0].method, args[0].url, (args[1] ?? {}) as FormOptions]
+ }
+
+ if (typeof args[0] === 'object') {
+ // Only options passed, use precognition values
+ return [method!, url!, args[0]]
+ }
+
+ // Method + URL
+ return [args[0] as Method, args[1] as string, {}]
+ }
+
+ // Track if defaults was called manually during onSuccess to avoid
+ // overriding user's custom defaults with automatic behavior.
+ let defaultsCalledInOnSuccess = false
+
+ const withPrecognition = (
+ form: InertiaForm & InternalFormProps,
+ method: Method | UrlMethodPair | (() => Method) | (() => UrlMethodPair),
+ url?: string | (() => string),
+ ) => {
+ resolvePrecognitionEndpoint = () => {
+ if (typeof method === 'function' || typeof url === 'function') {
+ const resolvedMethod = typeof method === 'function' ? method() : method
+ const resolvedUrl = typeof url === 'function' ? url() : url!
+
+ if (isUrlMethodPair(resolvedMethod)) {
+ return resolvedMethod
+ }
+
+ return { method: resolvedMethod as Method, url: resolvedUrl }
+ }
+
+ if (isUrlMethodPair(method)) {
+ return method
+ }
+
+ return { method: method as Method, url: url! as string }
+ }
+
+ let simpleValidationErrors = true
+
+ validator = createValidator((client) => {
+ const { method, url } = resolvePrecognitionEndpoint!()
+ const transformedData = transform(form.data()) as Record
+
+ return client[method](url, transformedData)
+ }, defaults)
+
+ // @ts-expect-error - TODO
+ const precognitionForm: InertiaPrecognitiveForm & InternalValidationProps = Object.assign(form, {
+ __touched: [],
+ __valid: [],
+ validating: false,
+ validator: () => validator!,
+ setValidationTimeout: (duration: number) => {
+ validator!.setTimeout(duration)
+
+ return form
+ },
+ validateFiles: () => {
+ validator!.validateFiles()
+
+ return form
+ },
+ withFullErrors: () => {
+ simpleValidationErrors = false
+
+ return form
+ },
+ withoutFileValidation: () => {
+ // @ts-expect-error - Not released yet...
+ validator!.withoutFileValidation()
+
+ return form
+ },
+ valid: (field: string) => precognitionForm.__valid.includes(field),
+ // @ts-expect-error - TODO
+ invalid: (field: string) => form.errors[field] !== undefined,
+ validate: (field?: string | NamedInputEvent | ValidationConfig, config?: ValidationConfig) => {
+ if (typeof field === 'object' && !('target' in field)) {
+ config = field
+ field = undefined
+ }
+
+ if (typeof field === 'undefined') {
+ validator!.validate(config)
+ } else {
+ field = resolveName(field)
+
+ const transformedData = transform(form.data()) as Record
+
+ validator!.validate(field, get(transformedData, field), config)
+ }
+
+ return form
+ },
+ touch: (...fields: string[]) => {
+ validator!.touch(fields)
+
+ return form
+ },
+ touched: (field?: string): boolean => {
+ if (typeof field === 'string') {
+ return precognitionForm.__touched.includes(field)
+ }
+
+ return precognitionForm.__touched.length > 0
+ },
+ })
+
+ validator
+ .on('validatingChanged', () => {
+ precognitionForm.validating = validator!.validating()
+ })
+ .on('validatedChanged', () => {
+ precognitionForm.__valid = validator!.valid()
+ })
+ .on('touchedChanged', () => {
+ precognitionForm.__touched = validator!.touched()
+ })
+ .on('errorsChanged', () => {
+ precognitionForm.errors = {} as FormDataErrors
+
+ const errors = (
+ simpleValidationErrors ? toSimpleValidationErrors(validator!.errors()) : validator!.errors()
+ ) as FormDataErrors
+
+ precognitionForm.setError(errors)
+ precognitionForm.__valid = validator!.valid()
+ })
+
+ return form
+ }
+
+ const _form = {
+ ...(restored ? restored.data : cloneDeep(defaults)),
+ isDirty: false,
+ errors: (restored ? restored.errors : {}) as FormDataErrors,
+ hasErrors: false,
+ processing: false,
+ progress: null,
+ wasSuccessful: false,
+ recentlySuccessful: false,
+ withPrecognition(
+ method: Method | UrlMethodPair | (() => Method) | (() => UrlMethodPair),
+ url?: string | (() => string),
+ ) {
+ return withPrecognition(this, method, url)
+ },
+ data() {
+ return (Object.keys(defaults) as Array>).reduce((carry, key) => {
+ return set(carry, key, get(this, key))
+ }, {} as Partial) as TForm
+ },
+ transform(callback: TransformCallback) {
+ transform = callback
+
+ return this
+ },
+ defaults(fieldOrFields?: FormDataKeys | Partial, maybeValue?: unknown) {
+ if (typeof data === 'function') {
+ throw new Error('You cannot call `defaults()` when using a function to define your form data.')
+ }
+
+ defaultsCalledInOnSuccess = true
+
+ if (typeof fieldOrFields === 'undefined') {
+ defaults = cloneDeep(this.data())
+ this.isDirty = false
+ } else {
+ defaults =
+ typeof fieldOrFields === 'string'
+ ? set(cloneDeep(defaults), fieldOrFields, maybeValue)
+ : Object.assign({}, cloneDeep(defaults), fieldOrFields)
+ }
+
+ return this
+ },
+ reset(...fields: string[]) {
+ const resolvedData = typeof data === 'function' ? cloneDeep(data()) : cloneDeep(defaults)
+ const clonedData = cloneDeep(resolvedData)
+ if (fields.length === 0) {
+ defaults = clonedData
+ Object.assign(this, resolvedData)
+ } else {
+ ;(fields as Array>)
+ .filter((key) => has(clonedData, key))
+ .forEach((key) => {
+ set(defaults, key, get(clonedData, key))
+ set(this, key, get(resolvedData, key))
+ })
+ }
+
+ validator?.reset(...fields)
+
+ return this
+ },
+ setError(fieldOrFields: FormDataKeys | FormDataErrors, maybeValue?: ErrorValue) {
+ const errors = typeof fieldOrFields === 'string' ? { [fieldOrFields]: maybeValue } : fieldOrFields
+
+ Object.assign(this.errors, errors)
+
+ this.hasErrors = Object.keys(this.errors).length > 0
+
+ // @ts-expect-error - validator may be null
+ validator?.setErrors(errors)
+
+ return this
+ },
+ clearErrors(...fields: string[]) {
+ this.errors = Object.keys(this.errors).reduce(
+ (carry, field) => ({
+ ...carry,
+ ...(fields.length > 0 && !fields.includes(field) ? { [field]: (this.errors as Errors)[field] } : {}),
+ }),
+ {},
+ ) as FormDataErrors
+
+ this.hasErrors = Object.keys(this.errors).length > 0
+
+ if (validator) {
+ if (fields.length === 0) {
+ validator.setErrors({})
+ } else {
+ fields.forEach(validator.forgetError)
+ }
+ }
+
+ return this
+ },
+ resetAndClearErrors(...fields: string[]) {
+ this.reset(...fields)
+ this.clearErrors(...fields)
+ return this
+ },
+ submit(...args: SubmitArgs) {
+ const [method, url, options] = parseSubmitArgs(args)
+
+ defaultsCalledInOnSuccess = false
+
+ const _options: VisitOptions = {
+ ...options,
+ onCancelToken: (token) => {
+ cancelToken = token
+
+ if (options.onCancelToken) {
+ return options.onCancelToken(token)
+ }
+ },
+ onBefore: (visit) => {
+ this.wasSuccessful = false
+ this.recentlySuccessful = false
+ clearTimeout(recentlySuccessfulTimeoutId)
+
+ if (options.onBefore) {
+ return options.onBefore(visit)
+ }
+ },
+ onStart: (visit) => {
+ this.processing = true
+
+ if (options.onStart) {
+ return options.onStart(visit)
+ }
+ },
+ onProgress: (event?: AxiosProgressEvent) => {
+ // @ts-expect-error - TODO
+ this.progress = event ?? null
+
+ if (options.onProgress) {
+ return options.onProgress(event)
+ }
+ },
+ onSuccess: async (page) => {
+ this.processing = false
+ this.progress = null
+ this.clearErrors()
+ this.wasSuccessful = true
+ this.recentlySuccessful = true
+ recentlySuccessfulTimeoutId = setTimeout(
+ () => (this.recentlySuccessful = false),
+ config.get('form.recentlySuccessfulDuration'),
+ )
+
+ const onSuccess = options.onSuccess ? await options.onSuccess(page) : null
+
+ if (!defaultsCalledInOnSuccess) {
+ defaults = cloneDeep(this.data())
+ this.isDirty = false
+ }
+
+ return onSuccess
+ },
+ onError: (errors) => {
+ this.processing = false
+ this.progress = null
+ this.clearErrors().setError(errors as FormDataErrors)
+
+ if (options.onError) {
+ return options.onError(errors)
+ }
+ },
+ onCancel: () => {
+ this.processing = false
+ this.progress = null
+
+ if (options.onCancel) {
+ return options.onCancel()
+ }
+ },
+ onFinish: (visit) => {
+ this.processing = false
+ this.progress = null
+ cancelToken = null
+
+ if (options.onFinish) {
+ return options.onFinish(visit)
+ }
+ },
+ }
+
+ const transformedData = transform(this.data()) as RequestPayload
+
+ if (method === 'delete') {
+ router.delete(url, { ..._options, data: transformedData })
+ } else {
+ router[method](url, transformedData, _options)
+ }
+ },
+ get(url: string, options: VisitOptions) {
+ this.submit('get', url, options)
+ },
+ post(url: string, options: VisitOptions) {
+ this.submit('post', url, options)
+ },
+ put(url: string, options: VisitOptions) {
+ this.submit('put', url, options)
+ },
+ patch(url: string, options: VisitOptions) {
+ this.submit('patch', url, options)
+ },
+ delete(url: string, options: VisitOptions) {
+ this.submit('delete', url, options)
+ },
+ cancel() {
+ if (cancelToken) {
+ cancelToken.cancel()
+ }
+ },
+ __rememberable: rememberKey === null,
+ __remember() {
+ return { data: this.data(), errors: this.errors }
+ },
+ __restore(restored: { data: TForm; errors: FormDataErrors }) {
+ Object.assign(this, restored.data)
+ this.setError(restored.errors)
+ },
+ }
+
+ const form = resolvePrecognitionEndpoint
+ ? reactive & InternalFormProps & InternalValidationProps>(
+ // @ts-expect-error - TODO
+ _form.withPrecognition(resolvePrecognitionEndpoint),
+ )
+ : // @ts-expect-error - TODO
+ reactive & InternalFormProps>(_form)
+
+ watch(
+ form,
+ (newValue) => {
+ // @ts-expect-error - TODO
+ form.isDirty = !isEqual(form.data(), defaults)
+ if (rememberKey) {
+ // @ts-expect-error - TODO
+ router.remember(cloneDeep(newValue.__remember()), rememberKey)
+ }
+ },
+ { immediate: true, deep: true },
+ )
+
+ return form
+}
diff --git a/packages/vue3/src/useForm.ts b/packages/vue3/src/useForm.ts
index 088ca8eba..94a1361d9 100644
--- a/packages/vue3/src/useForm.ts
+++ b/packages/vue3/src/useForm.ts
@@ -29,6 +29,8 @@ type FormOptions = Omit
type SubmitArgs = [Method, string, FormOptions?] | [UrlMethodPair, FormOptions?] | [FormOptions?]
type TransformCallback = (data: TForm) => object
+type WithPrecognitionArgs = [Method | (() => Method), string | (() => string)] | [UrlMethodPair | (() => UrlMethodPair)]
+
export interface InertiaFormProps {
isDirty: boolean
errors: FormDataErrors
@@ -54,40 +56,88 @@ export interface InertiaFormProps {
patch(url: string, options?: FormOptions): void
delete(url: string, options?: FormOptions): void
cancel(): void
- withPrecognition(
- method: Method | UrlMethodPair | (() => Method),
- url?: string | (() => string),
- ): InertiaPrecognitiveForm
+ withPrecognition(...args: WithPrecognitionArgs): InertiaPrecognitiveForm
}
-export interface InertiaFormValidationProps {
- validator: () => Validator
- validating: boolean
- valid(field: string): boolean
- invalid(field: string): boolean
- touched(field?: string): boolean
+type PrecognitionValidationConfig = ValidationConfig & {
+ only?: TKeys[]
+}
+
+export interface InertiaFormValidationProps {
+ invalid>(field: K): boolean
setValidationTimeout(duration: number): this
+ touch>(...fields: K[]): this
+ touched>(field?: K): boolean
+ valid>(field: K): boolean
+ validate>(
+ field?: K | NamedInputEvent | PrecognitionValidationConfig,
+ config?: PrecognitionValidationConfig,
+ ): this
validateFiles(): this
- withoutFileValidation(): this
- validate(field?: string | NamedInputEvent | ValidationConfig, config?: ValidationConfig): this
- touch(...fields: string[]): this
+ validating: boolean
+ validator: () => Validator
withFullErrors(): this
+ withoutFileValidation(): this
}
export type InertiaForm = TForm & InertiaFormProps
-export type InertiaPrecognitiveForm = InertiaForm & InertiaFormValidationProps
+export type InertiaPrecognitiveForm = TForm &
+ InertiaFormProps &
+ InertiaFormValidationProps
-export default function useForm>(data: TForm | (() => TForm)): InertiaForm
+type UseFormInertiaArguments = [data: TForm | (() => TForm)] | [rememberKey: string, data: TForm | (() => TForm)]
+type UseFormPrecognitionArguments =
+ | [urlMethodPair: UrlMethodPair | (() => UrlMethodPair), data: TForm | (() => TForm)]
+ | [method: Method | (() => Method), url: string | (() => string), data: TForm | (() => TForm)]
+type UseFormArguments = UseFormInertiaArguments | UseFormPrecognitionArguments
+
+export default function useForm>(
+ method: Method | (() => Method),
+ url: string | (() => string),
+ data: TForm | (() => TForm),
+): InertiaPrecognitiveForm
+export default function useForm>(
+ urlMethodPair: UrlMethodPair | (() => UrlMethodPair),
+ data: TForm | (() => TForm),
+): InertiaPrecognitiveForm
export default function useForm>(
rememberKey: string,
data: TForm | (() => TForm),
): InertiaForm
+export default function useForm>(data: TForm | (() => TForm)): InertiaForm
+//
+
export default function useForm>(
- rememberKeyOrData: string | TForm | (() => TForm),
- maybeData?: TForm | (() => TForm),
-): InertiaForm {
- const rememberKey = typeof rememberKeyOrData === 'string' ? rememberKeyOrData : null
- const data = (typeof rememberKeyOrData === 'string' ? maybeData : rememberKeyOrData) ?? {}
+ ...args: UseFormArguments
+): InertiaForm | InertiaPrecognitiveForm {
+ let precognitionEndpoint: (() => UrlMethodPair) | null = null
+
+ const rememberKeyOrData = args[0]
+ const maybeData = args[1]
+ const rememberKey = typeof args[0] === 'string' ? args[0] : null
+ let data = (typeof rememberKeyOrData === 'string' ? maybeData : rememberKeyOrData) ?? {}
+
+ if (args.length === 3) {
+ precognitionEndpoint = () => ({
+ method: typeof args[0] === 'function' ? args[0]() : args[0],
+ url: typeof args[1] === 'function' ? args[1]() : args[1],
+ })
+
+ data = args[2]
+ }
+
+ if (args.length === 2 && typeof args[0] !== 'string') {
+ precognitionEndpoint = () => {
+ if (typeof args[0] === 'function') {
+ return args[0]()
+ }
+
+ return args[0] as UrlMethodPair
+ }
+
+ data = args[1]
+ }
+
const restored = rememberKey
? (router.restore(rememberKey) as { data: TForm; errors: Record, string> })
: null
@@ -96,37 +146,21 @@ export default function useForm>(
let recentlySuccessfulTimeoutId: ReturnType
let transform: TransformCallback = (data) => data
- let precognitionUrl: string | (() => string) | null = null
- let precognitionMethod: Method | (() => Method) | null = null
let validator: Validator | null = null
- const parseSubmitArgs = (args: SubmitArgs): [Method, string, FormOptions] => {
- let method: Method = typeof precognitionMethod === 'function' ? precognitionMethod() : precognitionMethod!
- let url: string = typeof precognitionUrl === 'function' ? precognitionUrl() : precognitionUrl!
- let options: FormOptions = {}
-
- if (args.length === 0) {
- // No arguments passed, use precognition values
- return [method, url, options]
- }
-
- if (args.length === 3) {
- // All arguments passed
- return [args[0], args[1], args[2] ?? {}]
+ const parseSubmitArgs = (args: SubmitArgs): { method: Method; url: string; options: FormOptions } => {
+ if (args.length === 3 || (args.length === 2 && typeof args[0] === 'string')) {
+ // All arguments passed, or method + url
+ return { method: args[0], url: args[1], options: args[2] ?? {} }
}
if (isUrlMethodPair(args[0])) {
// Wayfinder + optional options
- return [args[0].method, args[0].url, (args[1] ?? {}) as FormOptions]
- }
-
- if (typeof args[0] === 'object') {
- // Only options passed, use precognition values
- return [method, url, args[0]]
+ return { ...args[0], options: (args[1] ?? {}) as FormOptions }
}
- // Method + URL
- return [args[0] as Method, args[1] as string, {}]
+ // No arguments, or only options passed, use precognition endpoint...
+ return { ...precognitionEndpoint!(), options: (args[0] ?? {}) as FormOptions }
}
// Track if defaults was called manually during onSuccess to avoid
@@ -142,20 +176,22 @@ export default function useForm>(
progress: null,
wasSuccessful: false,
recentlySuccessful: false,
- withPrecognition(method: Method | UrlMethodPair | (() => Method), url?: string | (() => string)) {
- if (isUrlMethodPair(method)) {
- precognitionMethod = method.method
- precognitionUrl = method.url
- } else {
- precognitionMethod = method
- precognitionUrl = url!
+ withPrecognition(...args: WithPrecognitionArgs) {
+ precognitionEndpoint = () => {
+ if (args.length === 2) {
+ return {
+ method: typeof args[0] === 'function' ? args[0]() : args[0],
+ url: typeof args[1] === 'function' ? args[1]() : args[1],
+ }
+ }
+
+ return typeof args[0] === 'function' ? args[0]() : args[0]
}
let simpleValidationErrors = true
validator = createValidator((client) => {
- const method = typeof precognitionMethod === 'function' ? precognitionMethod() : precognitionMethod!
- const url = typeof precognitionUrl === 'function' ? precognitionUrl() : precognitionUrl!
+ const { method, url } = precognitionEndpoint!()
const transformedData = transform(this.data()) as Record
@@ -327,7 +363,7 @@ export default function useForm>(
return this
},
submit(...args: SubmitArgs) {
- const [method, url, options] = parseSubmitArgs(args)
+ const { method, url, options } = parseSubmitArgs(args)
defaultsCalledInOnSuccess = false
@@ -460,5 +496,5 @@ export default function useForm>(
{ immediate: true, deep: true },
)
- return form
+ return precognitionEndpoint ? form.withPrecognition(precognitionEndpoint) : form
}
diff --git a/packages/vue3/test-app/Pages/FormHelper/Precognition/Instantiate.vue b/packages/vue3/test-app/Pages/FormHelper/Precognition/Instantiate.vue
new file mode 100644
index 000000000..351c52072
--- /dev/null
+++ b/packages/vue3/test-app/Pages/FormHelper/Precognition/Instantiate.vue
@@ -0,0 +1,77 @@
+
+
+