@vielzeug/forge 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/form.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"form.js","names":[],"sources":["../src/form.ts"],"sourcesContent":["import { abortable } from '@vielzeug/arsenal';\n\nimport { createField, type FieldAccess } from './_field.js';\nimport { createNotifier } from './_notify.js';\nimport {\n assertSafeKey,\n immutable,\n isRecord,\n type MetaRoot,\n readAtPath,\n resetAtPath,\n touchAll,\n writeAtPath,\n writeMeta,\n} from './core/path.js';\nimport { ForgeConfigError, ForgeDisposedError, ForgeSubmitError, ForgeValidationError } from './errors.js';\nimport type {\n Form,\n FormOptions,\n FormState,\n FormValidator,\n MaybePromise,\n SubmitResult,\n Unsubscribe,\n ValidationIssue,\n ValidationResult,\n} from './types.js';\n\ntype Validity = 'invalid' | 'unknown' | 'valid';\n\ntype InternalState<TValues extends Record<string, unknown>> = Readonly<{\n baseline: TValues;\n issues: readonly ValidationIssue[] | undefined;\n isSubmitting: boolean;\n isValidating: boolean;\n submitCount: number;\n touched: MetaRoot;\n validity: Validity;\n value: TValues;\n}>;\n\nfunction normalizeIssues(result: readonly ValidationIssue[] | undefined): readonly ValidationIssue[] | undefined {\n if (result === undefined || result.length === 0) return undefined;\n\n return Object.freeze(\n result.map((issue) => {\n if (!issue || typeof issue.message !== 'string' || !Array.isArray(issue.path)) {\n throw new ForgeConfigError('Validators must return issues with a string message and array path.');\n }\n\n const path = issue.path.map((part) => {\n if (typeof part === 'number') {\n if (!Number.isSafeInteger(part) || part < 0) throw new ForgeConfigError(`Invalid issue path index ${part}.`);\n return part;\n }\n if (typeof part === 'string') {\n assertSafeKey(part);\n return part;\n }\n throw new ForgeConfigError('Issue paths may contain only string keys and non-negative integer indexes.');\n });\n\n return Object.freeze({ message: issue.message, path: Object.freeze(path) });\n }),\n );\n}\n\nfunction makeFormState<TValues extends Record<string, unknown>>(current: InternalState<TValues>): FormState {\n return Object.freeze({\n formError: current.issues?.find((issue) => issue.path.length === 0)?.message,\n hasErrors: (current.issues?.length ?? 0) > 0,\n issues: current.issues,\n submitCount: current.submitCount,\n submitting: current.isSubmitting,\n touched: Object.keys(current.touched).length > 0,\n validating: current.isValidating,\n validity: current.validity,\n });\n}\n\n/** One immutable value tree and one explicit full-form validator keep form behavior locally understandable. */\nexport function createForm<TValues extends Record<string, unknown>>(options: FormOptions<TValues>): Form<TValues> {\n const initial = immutable(options.initialValues);\n const notifier = createNotifier<InternalState<TValues>>(options.onSubscriberError);\n const disposalController = new AbortController();\n let disposed = false;\n let dispatching = false;\n const pendingWrites: Array<(current: InternalState<TValues>) => InternalState<TValues>> = [];\n let validationController: AbortController | undefined;\n let revision = 0;\n let validatedSnapshot: { revision: number; value: TValues } | undefined;\n let current: InternalState<TValues> = {\n baseline: initial,\n isSubmitting: false,\n issues: undefined,\n isValidating: false,\n submitCount: 0,\n touched: {},\n validity: 'unknown',\n value: initial,\n };\n let publicState = makeFormState(current);\n\n function ensureActive(operation: string): void {\n if (disposed) throw new ForgeDisposedError(operation);\n }\n\n function write(update: (current: InternalState<TValues>) => InternalState<TValues>): void {\n pendingWrites.push(update);\n if (dispatching) return;\n\n dispatching = true;\n try {\n while (pendingWrites.length > 0) {\n current = Object.freeze(pendingWrites.shift()!(current));\n publicState = makeFormState(current);\n notifier.notify(current);\n }\n } finally {\n dispatching = false;\n }\n }\n\n function abortValidation(): void {\n validationController?.abort();\n }\n\n const access: FieldAccess = {\n abortValidation,\n addListener: notifier.add,\n ensureActive,\n invokeListener: notifier.call,\n readState: () => current,\n resetValue(path) {\n write((c) => {\n revision++;\n validatedSnapshot = undefined;\n return {\n ...c,\n touched: writeMeta(c.touched, path, false),\n validity: 'unknown',\n value: resetAtPath(c.value, c.baseline, path),\n };\n });\n },\n setTouched(path, touched) {\n write((c) => ({ ...c, touched: writeMeta(c.touched, path, touched) }));\n },\n setValue(path, next) {\n write((c) => {\n const previous = readAtPath(c.value, path);\n const value = typeof next === 'function' ? next(previous) : next;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, validity: 'unknown', value: writeAtPath(c.value, path, value) };\n });\n },\n };\n\n async function validate(externalSignal?: AbortSignal): Promise<ValidationResult> {\n ensureActive('validate');\n abortValidation();\n\n const controller = new AbortController();\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n const snapshot = current.value;\n const snapshotRevision = revision;\n validationController = controller;\n validatedSnapshot = undefined;\n write((c) => ({ ...c, isValidating: true }));\n\n try {\n if (signal.aborted || revision !== snapshotRevision) return Object.freeze({ status: 'aborted' });\n const result: readonly ValidationIssue[] | undefined = options.validate\n ? await abortable(\n Promise.resolve(options.validate(snapshot as Parameters<FormValidator<TValues>>[0], signal)),\n signal,\n )\n : undefined;\n\n if (signal.aborted || validationController !== controller || revision !== snapshotRevision) {\n return Object.freeze({ status: 'aborted' });\n }\n\n const issues = normalizeIssues(result);\n const validity: Validity = issues === undefined ? 'valid' : 'invalid';\n validationController = undefined;\n write((c) => ({ ...c, issues, isValidating: false, validity }));\n\n if (signal.aborted || revision !== snapshotRevision || validationController !== undefined) {\n return Object.freeze({ status: 'aborted' });\n }\n\n validatedSnapshot = validity === 'valid' ? { revision, value: snapshot } : undefined;\n return validity === 'valid'\n ? Object.freeze({ status: 'valid' })\n : Object.freeze({ issues: issues as readonly ValidationIssue[], status: 'invalid' });\n } catch (error) {\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n throw new ForgeValidationError('Form validation failed.', { cause: error });\n } finally {\n if (validationController === controller) {\n validationController = undefined;\n if (!disposed) write((c) => ({ ...c, isValidating: false }));\n }\n }\n }\n\n const form: Form<TValues> = {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose() {\n if (disposed) return;\n\n disposed = true;\n pendingWrites.length = 0;\n validatedSnapshot = undefined;\n disposalController.abort();\n abortValidation();\n notifier.clear();\n },\n get disposed() {\n return disposed;\n },\n field(key) {\n assertSafeKey(key);\n if (!isRecord(current.value)) throw new ForgeConfigError('Form value must be an object.');\n\n return createField<TValues[typeof key]>([key], access);\n },\n reset(next) {\n ensureActive('reset');\n abortValidation();\n\n const replacement = next === undefined ? undefined : immutable(next);\n write((c) => {\n const baseline = replacement ?? c.baseline;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, baseline, issues: undefined, touched: {}, validity: 'unknown', value: baseline };\n });\n },\n set(next) {\n ensureActive('set');\n abortValidation();\n\n write((c) => {\n const value =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => TValues)(c.value as Form<TValues>['value'])\n : next;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(value) };\n });\n },\n get state() {\n return publicState;\n },\n async submit<TResult = void>(\n handler: (values: Form<TValues>['value'], signal: AbortSignal) => MaybePromise<TResult>,\n externalSignal?: AbortSignal,\n ): Promise<SubmitResult<TResult>> {\n ensureActive('submit');\n\n if (current.isSubmitting) {\n throw new ForgeSubmitError('submit() called while a submission is already in progress');\n }\n\n const signal = externalSignal\n ? AbortSignal.any([disposalController.signal, externalSignal])\n : disposalController.signal;\n\n write((c) => ({\n ...c,\n isSubmitting: true,\n submitCount: c.submitCount + 1,\n touched: touchAll(c.value),\n }));\n\n try {\n const result = await validate(externalSignal);\n\n if (result.status === 'aborted') return Object.freeze({ status: 'aborted' });\n\n if (result.status === 'invalid') {\n return Object.freeze({ issues: result.issues, status: 'invalid' });\n }\n\n const validated = validatedSnapshot;\n if (!validated || validated.revision !== revision || signal.aborted) {\n return Object.freeze({ status: 'aborted' });\n }\n\n try {\n return Object.freeze({\n status: 'ok',\n value: await handler(validated.value as Form<TValues>['value'], signal),\n });\n } catch (error) {\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n throw error;\n }\n } finally {\n if (!disposed) write((c) => ({ ...c, isSubmitting: false }));\n }\n },\n subscribe(listener, subscribeOptions = {}): Unsubscribe {\n ensureActive('subscribe');\n\n if (subscribeOptions.immediate) notifier.call(() => listener(publicState));\n\n return notifier.add(() => listener(publicState));\n },\n [Symbol.dispose]() {\n form.dispose();\n },\n validate,\n get value() {\n return current.value as Form<TValues>['value'];\n },\n };\n\n return form;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"form.js","names":[],"sources":["../src/form.ts"],"sourcesContent":["import { abortable } from '@vielzeug/arsenal';\n\nimport { createField, type FieldAccess } from './_field.js';\nimport { createNotifier } from './_notify.js';\nimport {\n assertSafeKey,\n immutable,\n isRecord,\n type MetaRoot,\n readAtPath,\n resetAtPath,\n touchAll,\n writeAtPath,\n writeMeta,\n} from './core/path.js';\nimport { ForgeConfigError, ForgeDisposedError, ForgeSubmitError, ForgeValidationError } from './errors.js';\nimport type {\n Form,\n FormOptions,\n FormState,\n FormValidator,\n MaybePromise,\n SubmitResult,\n Unsubscribe,\n ValidationIssue,\n ValidationResult,\n} from './types.js';\n\ntype Validity = 'invalid' | 'unknown' | 'valid';\n\ntype InternalState<TValues extends Record<string, unknown>> = Readonly<{\n baseline: TValues;\n issues: readonly ValidationIssue[] | undefined;\n isSubmitting: boolean;\n isValidating: boolean;\n submitCount: number;\n touched: MetaRoot;\n validity: Validity;\n value: TValues;\n}>;\n\nfunction normalizeIssues(result: readonly ValidationIssue[] | undefined): readonly ValidationIssue[] | undefined {\n if (result === undefined || result.length === 0) return undefined;\n\n return Object.freeze(\n result.map((issue) => {\n if (!issue || typeof issue.message !== 'string' || !Array.isArray(issue.path)) {\n throw new ForgeConfigError('Validators must return issues with a string message and array path.');\n }\n\n const path = issue.path.map((part) => {\n if (typeof part === 'number') {\n if (!Number.isSafeInteger(part) || part < 0) throw new ForgeConfigError(`Invalid issue path index ${part}.`);\n return part;\n }\n if (typeof part === 'string') {\n assertSafeKey(part);\n return part;\n }\n throw new ForgeConfigError('Issue paths may contain only string keys and non-negative integer indexes.');\n });\n\n return Object.freeze({ message: issue.message, path: Object.freeze(path) });\n }),\n );\n}\n\nfunction makeFormState<TValues extends Record<string, unknown>>(current: InternalState<TValues>): FormState {\n return Object.freeze({\n formError: current.issues?.find((issue) => issue.path.length === 0)?.message,\n hasErrors: (current.issues?.length ?? 0) > 0,\n issues: current.issues,\n submitCount: current.submitCount,\n submitting: current.isSubmitting,\n touched: Object.keys(current.touched).length > 0,\n validating: current.isValidating,\n validity: current.validity,\n });\n}\n\n/** One immutable value tree and one explicit full-form validator keep form behavior locally understandable. */\nexport function createForm<TValues extends Record<string, unknown>>(options: FormOptions<TValues>): Form<TValues> {\n const normalize = options.normalize;\n const prepare = (value: unknown): unknown => (normalize ? normalize(value) : value);\n const initial = immutable(prepare(options.initialValues) as TValues);\n const notifier = createNotifier<InternalState<TValues>>(options.onSubscriberError);\n const disposalController = new AbortController();\n let disposed = false;\n let dispatching = false;\n const pendingWrites: Array<(current: InternalState<TValues>) => InternalState<TValues>> = [];\n let validationController: AbortController | undefined;\n let revision = 0;\n let validatedSnapshot: { revision: number; value: TValues } | undefined;\n let current: InternalState<TValues> = {\n baseline: initial,\n isSubmitting: false,\n issues: undefined,\n isValidating: false,\n submitCount: 0,\n touched: {},\n validity: 'unknown',\n value: initial,\n };\n let publicState = makeFormState(current);\n\n function ensureActive(operation: string): void {\n if (disposed) throw new ForgeDisposedError(operation);\n }\n\n function write(update: (current: InternalState<TValues>) => InternalState<TValues>): void {\n pendingWrites.push(update);\n if (dispatching) return;\n\n dispatching = true;\n try {\n while (pendingWrites.length > 0) {\n current = Object.freeze(pendingWrites.shift()!(current));\n publicState = makeFormState(current);\n notifier.notify(current);\n }\n } finally {\n dispatching = false;\n }\n }\n\n function abortValidation(): void {\n validationController?.abort();\n }\n\n const access: FieldAccess = {\n abortValidation,\n addListener: notifier.add,\n ensureActive,\n invokeListener: notifier.call,\n readState: () => current,\n resetValue(path) {\n write((c) => {\n revision++;\n validatedSnapshot = undefined;\n return {\n ...c,\n touched: writeMeta(c.touched, path, false),\n validity: 'unknown',\n value: resetAtPath(c.value, c.baseline, path),\n };\n });\n },\n setTouched(path, touched) {\n write((c) => ({ ...c, touched: writeMeta(c.touched, path, touched) }));\n },\n setValue(path, next) {\n write((c) => {\n const previous = readAtPath(c.value, path);\n const raw = typeof next === 'function' ? next(previous) : next;\n const value = prepare(raw);\n revision++;\n validatedSnapshot = undefined;\n return { ...c, validity: 'unknown', value: writeAtPath(c.value, path, value) };\n });\n },\n };\n\n async function validate(externalSignal?: AbortSignal): Promise<ValidationResult> {\n ensureActive('validate');\n abortValidation();\n\n const controller = new AbortController();\n const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n const snapshot = current.value;\n const snapshotRevision = revision;\n validationController = controller;\n validatedSnapshot = undefined;\n write((c) => ({ ...c, isValidating: true }));\n\n try {\n if (signal.aborted || revision !== snapshotRevision) return Object.freeze({ status: 'aborted' });\n const result: readonly ValidationIssue[] | undefined = options.validate\n ? await abortable(\n Promise.resolve(options.validate(snapshot as Parameters<FormValidator<TValues>>[0], signal)),\n signal,\n )\n : undefined;\n\n if (signal.aborted || validationController !== controller || revision !== snapshotRevision) {\n return Object.freeze({ status: 'aborted' });\n }\n\n const issues = normalizeIssues(result);\n const validity: Validity = issues === undefined ? 'valid' : 'invalid';\n validationController = undefined;\n write((c) => ({ ...c, issues, isValidating: false, validity }));\n\n if (signal.aborted || revision !== snapshotRevision || validationController !== undefined) {\n return Object.freeze({ status: 'aborted' });\n }\n\n validatedSnapshot = validity === 'valid' ? { revision, value: snapshot } : undefined;\n return validity === 'valid'\n ? Object.freeze({ status: 'valid' })\n : Object.freeze({ issues: issues as readonly ValidationIssue[], status: 'invalid' });\n } catch (error) {\n if (signal.aborted || validationController !== controller) return Object.freeze({ status: 'aborted' });\n throw new ForgeValidationError('Form validation failed.', { cause: error });\n } finally {\n if (validationController === controller) {\n validationController = undefined;\n if (!disposed) write((c) => ({ ...c, isValidating: false }));\n }\n }\n }\n\n const form: Form<TValues> = {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose() {\n if (disposed) return;\n\n disposed = true;\n pendingWrites.length = 0;\n validatedSnapshot = undefined;\n disposalController.abort();\n abortValidation();\n notifier.clear();\n },\n get disposed() {\n return disposed;\n },\n field(key) {\n assertSafeKey(key);\n if (!isRecord(current.value)) throw new ForgeConfigError('Form value must be an object.');\n\n return createField<TValues[typeof key]>([key], access);\n },\n patch(next) {\n ensureActive('patch');\n abortValidation();\n\n write((c) => {\n const partial =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => Partial<TValues>)(c.value as Form<TValues>['value'])\n : next;\n const merged = { ...c.value, ...partial };\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(merged) as TValues) };\n });\n },\n reset(next) {\n ensureActive('reset');\n abortValidation();\n\n const replacement = next === undefined ? undefined : immutable(prepare(next) as TValues);\n write((c) => {\n const baseline = replacement ?? c.baseline;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, baseline, issues: undefined, touched: {}, validity: 'unknown', value: baseline };\n });\n },\n set(next) {\n ensureActive('set');\n abortValidation();\n\n write((c) => {\n const value =\n typeof next === 'function'\n ? (next as (previous: Form<TValues>['value']) => TValues)(c.value as Form<TValues>['value'])\n : next;\n revision++;\n validatedSnapshot = undefined;\n return { ...c, issues: undefined, validity: 'unknown', value: immutable(prepare(value) as TValues) };\n });\n },\n get state() {\n return publicState;\n },\n async submit<TResult = void>(\n handler: (values: Form<TValues>['value'], signal: AbortSignal) => MaybePromise<TResult>,\n externalSignal?: AbortSignal,\n ): Promise<SubmitResult<TResult>> {\n ensureActive('submit');\n\n if (current.isSubmitting) {\n throw new ForgeSubmitError('submit() called while a submission is already in progress');\n }\n\n const signal = externalSignal\n ? AbortSignal.any([disposalController.signal, externalSignal])\n : disposalController.signal;\n\n write((c) => ({\n ...c,\n isSubmitting: true,\n submitCount: c.submitCount + 1,\n touched: touchAll(c.value),\n }));\n\n try {\n const result = await validate(externalSignal);\n\n if (result.status === 'aborted') return Object.freeze({ status: 'aborted' });\n\n if (result.status === 'invalid') {\n return Object.freeze({ issues: result.issues, status: 'invalid' });\n }\n\n const validated = validatedSnapshot;\n if (!validated || validated.revision !== revision || signal.aborted) {\n return Object.freeze({ status: 'aborted' });\n }\n\n try {\n return Object.freeze({\n status: 'ok',\n value: await handler(validated.value as Form<TValues>['value'], signal),\n });\n } catch (error) {\n if (signal.aborted) return Object.freeze({ status: 'aborted' });\n\n throw error;\n }\n } finally {\n if (!disposed) write((c) => ({ ...c, isSubmitting: false }));\n }\n },\n subscribe(listener, subscribeOptions = {}): Unsubscribe {\n ensureActive('subscribe');\n\n if (subscribeOptions.immediate) notifier.call(() => listener(publicState));\n\n return notifier.add(() => listener(publicState));\n },\n [Symbol.dispose]() {\n form.dispose();\n },\n validate,\n get value() {\n return current.value as Form<TValues>['value'];\n },\n };\n\n return form;\n}\n"],"mappings":";;;;;;AAyCA,SAAS,EAAgB,GAAwF;CAC3G,UAAW,KAAA,KAAa,EAAO,WAAW,GAE9C,OAAO,OAAO,OACZ,EAAO,KAAK,MAAU;EACpB,IAAI,CAAC,KAAS,OAAO,EAAM,WAAY,YAAY,CAAC,MAAM,QAAQ,EAAM,IAAI,GAC1E,MAAM,IAAI,EAAiB,qEAAqE;EAGlG,IAAM,IAAO,EAAM,KAAK,KAAK,MAAS;GACpC,IAAI,OAAO,KAAS,UAAU;IAC5B,IAAI,CAAC,OAAO,cAAc,CAAI,KAAK,IAAO,GAAG,MAAM,IAAI,EAAiB,4BAA4B,EAAK,EAAE;IAC3G,OAAO;GACT;GACA,IAAI,OAAO,KAAS,UAElB,OADA,EAAc,CAAI,GACX;GAET,MAAM,IAAI,EAAiB,4EAA4E;EACzG,CAAC;EAED,OAAO,OAAO,OAAO;GAAE,SAAS,EAAM;GAAS,MAAM,OAAO,OAAO,CAAI;EAAE,CAAC;CAC5E,CAAC,CACH;AACF;AAEA,SAAS,EAAuD,GAA4C;CAC1G,OAAO,OAAO,OAAO;EACnB,WAAW,EAAQ,QAAQ,MAAM,MAAU,EAAM,KAAK,WAAW,CAAC,CAAC,EAAE;EACrE,YAAY,EAAQ,QAAQ,UAAU,KAAK;EAC3C,QAAQ,EAAQ;EAChB,aAAa,EAAQ;EACrB,YAAY,EAAQ;EACpB,SAAS,OAAO,KAAK,EAAQ,OAAO,CAAC,CAAC,SAAS;EAC/C,YAAY,EAAQ;EACpB,UAAU,EAAQ;CACpB,CAAC;AACH;AAGA,SAAgB,EAAoD,GAA8C;CAChH,IAAM,IAAY,EAAQ,WACpB,KAAW,MAA6B,IAAY,EAAU,CAAK,IAAI,GACvE,IAAU,EAAU,EAAQ,EAAQ,aAAa,CAAY,GAC7D,IAAW,EAAuC,EAAQ,iBAAiB,GAC3E,IAAqB,IAAI,gBAAgB,GAC3C,IAAW,IACX,IAAc,IACZ,IAAoF,CAAC,GACvF,GACA,IAAW,GACX,GACA,IAAkC;EACpC,UAAU;EACV,cAAc;EACd,QAAQ,KAAA;EACR,cAAc;EACd,aAAa;EACb,SAAS,CAAC;EACV,UAAU;EACV,OAAO;CACT,GACI,IAAc,EAAc,CAAO;CAEvC,SAAS,EAAa,GAAyB;EAC7C,IAAI,GAAU,MAAM,IAAI,EAAmB,CAAS;CACtD;CAEA,SAAS,EAAM,GAA2E;EACxF,MAAc,KAAK,CAAM,GACrB,IAEJ;OAAc;GACd,IAAI;IACF,OAAO,EAAc,SAAS,IAG5B,AAFA,IAAU,OAAO,OAAO,EAAc,MAAM,CAAC,CAAE,CAAO,CAAC,GACvD,IAAc,EAAc,CAAO,GACnC,EAAS,OAAO,CAAO;GAE3B,UAAU;IACR,IAAc;GAChB;EATc;CAUhB;CAEA,SAAS,IAAwB;EAC/B,GAAsB,MAAM;CAC9B;CAEA,IAAM,IAAsB;EAC1B;EACA,aAAa,EAAS;EACtB;EACA,gBAAgB,EAAS;EACzB,iBAAiB;EACjB,WAAW,GAAM;GACf,GAAO,OACL,KACA,IAAoB,KAAA,GACb;IACL,GAAG;IACH,SAAS,EAAU,EAAE,SAAS,GAAM,EAAK;IACzC,UAAU;IACV,OAAO,EAAY,EAAE,OAAO,EAAE,UAAU,CAAI;GAC9C,EACD;EACH;EACA,WAAW,GAAM,GAAS;GACxB,GAAO,OAAO;IAAE,GAAG;IAAG,SAAS,EAAU,EAAE,SAAS,GAAM,CAAO;GAAE,EAAE;EACvE;EACA,SAAS,GAAM,GAAM;GACnB,GAAO,MAAM;IACX,IAAM,IAAW,EAAW,EAAE,OAAO,CAAI,GACnC,IAAM,OAAO,KAAS,aAAa,EAAK,CAAQ,IAAI,GACpD,IAAQ,EAAQ,CAAG;IAGzB,OAFA,KACA,IAAoB,KAAA,GACb;KAAE,GAAG;KAAG,UAAU;KAAW,OAAO,EAAY,EAAE,OAAO,GAAM,CAAK;IAAE;GAC/E,CAAC;EACH;CACF;CAEA,eAAe,EAAS,GAAyD;EAE/E,AADA,EAAa,UAAU,GACvB,EAAgB;EAEhB,IAAM,IAAa,IAAI,gBAAgB,GACjC,IAAS,IAAiB,YAAY,IAAI,CAAC,EAAW,QAAQ,CAAc,CAAC,IAAI,EAAW;EAClG,IAAI,EAAO,SAAS,OAAO,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;EAE9D,IAAM,IAAW,EAAQ,OACnB,IAAmB;EAGzB,AAFA,IAAuB,GACvB,IAAoB,KAAA,GACpB,GAAO,OAAO;GAAE,GAAG;GAAG,cAAc;EAAK,EAAE;EAE3C,IAAI;GACF,IAAI,EAAO,WAAW,MAAa,GAAkB,OAAO,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;GAC/F,IAAM,IAAiD,EAAQ,WAC3D,MAAM,EACJ,QAAQ,QAAQ,EAAQ,SAAS,GAAmD,CAAM,CAAC,GAC3F,CACF,IACA,KAAA;GAEJ,IAAI,EAAO,WAAW,MAAyB,KAAc,MAAa,GACxE,OAAO,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;GAG5C,IAAM,IAAS,EAAgB,CAAM,GAC/B,IAAqB,MAAW,KAAA,IAAY,UAAU;GAS5D,OARA,IAAuB,KAAA,GACvB,GAAO,OAAO;IAAE,GAAG;IAAG;IAAQ,cAAc;IAAO;GAAS,EAAE,GAE1D,EAAO,WAAW,MAAa,KAAoB,MAAyB,KAAA,IACvE,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC,KAG5C,IAAoB,MAAa,UAAU;IAAE;IAAU,OAAO;GAAS,IAAI,KAAA,GAEvE,OAAO,OADJ,MAAa,UACF,EAAE,QAAQ,QAAQ,IAClB;IAAU;IAAsC,QAAQ;GAAU,CAAC;EACvF,SAAS,GAAO;GACd,IAAI,EAAO,WAAW,MAAyB,GAAY,OAAO,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;GACrG,MAAM,IAAI,EAAqB,2BAA2B,EAAE,OAAO,EAAM,CAAC;EAC5E,UAAU;GACR,AAAI,MAAyB,MAC3B,IAAuB,KAAA,GAClB,KAAU,GAAO,OAAO;IAAE,GAAG;IAAG,cAAc;GAAM,EAAE;EAE/D;CACF;CAEA,IAAM,IAAsB;EAC1B,IAAI,iBAAiB;GACnB,OAAO,EAAmB;EAC5B;EACA,UAAU;GACJ,MAEJ,IAAW,IACX,EAAc,SAAS,GACvB,IAAoB,KAAA,GACpB,EAAmB,MAAM,GACzB,EAAgB,GAChB,EAAS,MAAM;EACjB;EACA,IAAI,WAAW;GACb,OAAO;EACT;EACA,MAAM,GAAK;GAET,IADA,EAAc,CAAG,GACb,CAAC,EAAS,EAAQ,KAAK,GAAG,MAAM,IAAI,EAAiB,+BAA+B;GAExF,OAAO,EAAiC,CAAC,CAAG,GAAG,CAAM;EACvD;EACA,MAAM,GAAM;GAIV,AAHA,EAAa,OAAO,GACpB,EAAgB,GAEhB,GAAO,MAAM;IACX,IAAM,IACJ,OAAO,KAAS,aACX,EAAgE,EAAE,KAA+B,IAClG,GACA,IAAS;KAAE,GAAG,EAAE;KAAO,GAAG;IAAQ;IAGxC,OAFA,KACA,IAAoB,KAAA,GACb;KAAE,GAAG;KAAG,QAAQ,KAAA;KAAW,UAAU;KAAW,OAAO,EAAU,EAAQ,CAAM,CAAY;IAAE;GACtG,CAAC;EACH;EACA,MAAM,GAAM;GAEV,AADA,EAAa,OAAO,GACpB,EAAgB;GAEhB,IAAM,IAAc,MAAS,KAAA,IAAY,KAAA,IAAY,EAAU,EAAQ,CAAI,CAAY;GACvF,GAAO,MAAM;IACX,IAAM,IAAW,KAAe,EAAE;IAGlC,OAFA,KACA,IAAoB,KAAA,GACb;KAAE,GAAG;KAAG;KAAU,QAAQ,KAAA;KAAW,SAAS,CAAC;KAAG,UAAU;KAAW,OAAO;IAAS;GAChG,CAAC;EACH;EACA,IAAI,GAAM;GAIR,AAHA,EAAa,KAAK,GAClB,EAAgB,GAEhB,GAAO,MAAM;IACX,IAAM,IACJ,OAAO,KAAS,aACX,EAAuD,EAAE,KAA+B,IACzF;IAGN,OAFA,KACA,IAAoB,KAAA,GACb;KAAE,GAAG;KAAG,QAAQ,KAAA;KAAW,UAAU;KAAW,OAAO,EAAU,EAAQ,CAAK,CAAY;IAAE;GACrG,CAAC;EACH;EACA,IAAI,QAAQ;GACV,OAAO;EACT;EACA,MAAM,OACJ,GACA,GACgC;GAGhC,IAFA,EAAa,QAAQ,GAEjB,EAAQ,cACV,MAAM,IAAI,EAAiB,2DAA2D;GAGxF,IAAM,IAAS,IACX,YAAY,IAAI,CAAC,EAAmB,QAAQ,CAAc,CAAC,IAC3D,EAAmB;GAEvB,GAAO,OAAO;IACZ,GAAG;IACH,cAAc;IACd,aAAa,EAAE,cAAc;IAC7B,SAAS,EAAS,EAAE,KAAK;GAC3B,EAAE;GAEF,IAAI;IACF,IAAM,IAAS,MAAM,EAAS,CAAc;IAE5C,IAAI,EAAO,WAAW,WAAW,OAAO,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;IAE3E,IAAI,EAAO,WAAW,WACpB,OAAO,OAAO,OAAO;KAAE,QAAQ,EAAO;KAAQ,QAAQ;IAAU,CAAC;IAGnE,IAAM,IAAY;IAClB,IAAI,CAAC,KAAa,EAAU,aAAa,KAAY,EAAO,SAC1D,OAAO,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;IAG5C,IAAI;KACF,OAAO,OAAO,OAAO;MACnB,QAAQ;MACR,OAAO,MAAM,EAAQ,EAAU,OAAiC,CAAM;KACxE,CAAC;IACH,SAAS,GAAO;KACd,IAAI,EAAO,SAAS,OAAO,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;KAE9D,MAAM;IACR;GACF,UAAU;IACR,AAAK,KAAU,GAAO,OAAO;KAAE,GAAG;KAAG,cAAc;IAAM,EAAE;GAC7D;EACF;EACA,UAAU,GAAU,IAAmB,CAAC,GAAgB;GAKtD,OAJA,EAAa,WAAW,GAEpB,EAAiB,aAAW,EAAS,WAAW,EAAS,CAAW,CAAC,GAElE,EAAS,UAAU,EAAS,CAAW,CAAC;EACjD;EACA,CAAC,OAAO,WAAW;GACjB,EAAK,QAAQ;EACf;EACA;EACA,IAAI,QAAQ;GACV,OAAO,EAAQ;EACjB;CACF;CAEA,OAAO;AACT"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./form.cjs");exports.ForgeConfigError=e.ForgeConfigError,exports.ForgeDisposedError=e.ForgeDisposedError,exports.ForgeError=e.ForgeError,exports.ForgeSubmitError=e.ForgeSubmitError,exports.ForgeValidationError=e.ForgeValidationError,exports.createForm=t.createForm;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./form.cjs"),n=require("./plain.cjs");exports.ForgeConfigError=e.ForgeConfigError,exports.ForgeDisposedError=e.ForgeDisposedError,exports.ForgeError=e.ForgeError,exports.ForgeSubmitError=e.ForgeSubmitError,exports.ForgeValidationError=e.ForgeValidationError,exports.createForm=t.createForm,exports.toPlainValues=n.toPlainValues;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { ForgeConfigError, ForgeDisposedError, ForgeError, ForgeSubmitError, ForgeValidationError } from './errors.js';
2
2
  export { createForm } from './form.js';
3
+ export { toPlainValues } from './plain.js';
3
4
  export * from './types.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,UAAU,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,UAAU,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { ForgeConfigError as e, ForgeDisposedError as t, ForgeError as n, ForgeSubmitError as r, ForgeValidationError as i } from "./errors.js";
2
2
  import { createForm as a } from "./form.js";
3
- export { e as ForgeConfigError, t as ForgeDisposedError, n as ForgeError, r as ForgeSubmitError, i as ForgeValidationError, a as createForm };
3
+ import { toPlainValues as o } from "./plain.js";
4
+ export { e as ForgeConfigError, t as ForgeDisposedError, n as ForgeError, r as ForgeSubmitError, i as ForgeValidationError, a as createForm, o as toPlainValues };
package/dist/plain.cjs ADDED
@@ -0,0 +1,2 @@
1
+ const e=require("./core/path.cjs");function t(t){return e.toPlainValues(t)}exports.toPlainValues=t;
2
+ //# sourceMappingURL=plain.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain.cjs","names":[],"sources":["../src/plain.ts"],"sourcesContent":["import { toPlainValues as flattenValue } from './core/path.js';\n\n/**\n * Deeply converts a value into the plain shape Forge accepts: primitives pass through,\n * `Date` is cloned, `File`/`Blob` keep identity, arrays map, and class instances flatten to\n * their own enumerable entries. Unsafe keys (`__proto__`, `constructor`, `prototype`) are\n * dropped, so the output always passes value validation. Use as the `normalize` option of\n * `createForm` to hold domain-model classes in form state.\n *\n * Lossy conversions, applied instead of the error an un-normalized write would throw:\n * `Map`, `Set`, and other keyless built-ins flatten to `{}` (their entries live on the\n * prototype); sparse array slots are dropped, which shifts later indexes; `NaN`, `Infinity`,\n * functions, and circular branches become `undefined`. Normalize model classes, not\n * arbitrary graphs. The type parameter is a convenience cast — the flattened shape differs\n * from the input type whenever class instances are present.\n *\n * This is a public wrapper so the emitted declaration does not name `@vielzeug/arsenal`\n * symbols: exporting the `core/path.ts` original directly pulls arsenal's extensionless\n * re-exports into the NodeNext declaration graph that `test:types` compiles. Keep the\n * implementation in `core/path.ts` beside the `immutable()` leaf rules it mirrors.\n */\nexport function toPlainValues<T>(value: T): T {\n return flattenValue(value);\n}\n"],"mappings":"mCAqBA,SAAgB,EAAiB,EAAa,CAC5C,OAAO,EAAA,cAAa,CAAK,CAC3B"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Deeply converts a value into the plain shape Forge accepts: primitives pass through,
3
+ * `Date` is cloned, `File`/`Blob` keep identity, arrays map, and class instances flatten to
4
+ * their own enumerable entries. Unsafe keys (`__proto__`, `constructor`, `prototype`) are
5
+ * dropped, so the output always passes value validation. Use as the `normalize` option of
6
+ * `createForm` to hold domain-model classes in form state.
7
+ *
8
+ * Lossy conversions, applied instead of the error an un-normalized write would throw:
9
+ * `Map`, `Set`, and other keyless built-ins flatten to `{}` (their entries live on the
10
+ * prototype); sparse array slots are dropped, which shifts later indexes; `NaN`, `Infinity`,
11
+ * functions, and circular branches become `undefined`. Normalize model classes, not
12
+ * arbitrary graphs. The type parameter is a convenience cast — the flattened shape differs
13
+ * from the input type whenever class instances are present.
14
+ *
15
+ * This is a public wrapper so the emitted declaration does not name `@vielzeug/arsenal`
16
+ * symbols: exporting the `core/path.ts` original directly pulls arsenal's extensionless
17
+ * re-exports into the NodeNext declaration graph that `test:types` compiles. Keep the
18
+ * implementation in `core/path.ts` beside the `immutable()` leaf rules it mirrors.
19
+ */
20
+ export declare function toPlainValues<T>(value: T): T;
21
+ //# sourceMappingURL=plain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain.d.ts","sourceRoot":"","sources":["../src/plain.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAE5C"}
package/dist/plain.js ADDED
@@ -0,0 +1,9 @@
1
+ import { toPlainValues as e } from "./core/path.js";
2
+ //#region src/plain.ts
3
+ function t(t) {
4
+ return e(t);
5
+ }
6
+ //#endregion
7
+ export { t as toPlainValues };
8
+
9
+ //# sourceMappingURL=plain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain.js","names":[],"sources":["../src/plain.ts"],"sourcesContent":["import { toPlainValues as flattenValue } from './core/path.js';\n\n/**\n * Deeply converts a value into the plain shape Forge accepts: primitives pass through,\n * `Date` is cloned, `File`/`Blob` keep identity, arrays map, and class instances flatten to\n * their own enumerable entries. Unsafe keys (`__proto__`, `constructor`, `prototype`) are\n * dropped, so the output always passes value validation. Use as the `normalize` option of\n * `createForm` to hold domain-model classes in form state.\n *\n * Lossy conversions, applied instead of the error an un-normalized write would throw:\n * `Map`, `Set`, and other keyless built-ins flatten to `{}` (their entries live on the\n * prototype); sparse array slots are dropped, which shifts later indexes; `NaN`, `Infinity`,\n * functions, and circular branches become `undefined`. Normalize model classes, not\n * arbitrary graphs. The type parameter is a convenience cast — the flattened shape differs\n * from the input type whenever class instances are present.\n *\n * This is a public wrapper so the emitted declaration does not name `@vielzeug/arsenal`\n * symbols: exporting the `core/path.ts` original directly pulls arsenal's extensionless\n * re-exports into the NodeNext declaration graph that `test:types` compiles. Keep the\n * implementation in `core/path.ts` beside the `immutable()` leaf rules it mirrors.\n */\nexport function toPlainValues<T>(value: T): T {\n return flattenValue(value);\n}\n"],"mappings":";;AAqBA,SAAgB,EAAiB,GAAa;CAC5C,OAAO,EAAa,CAAK;AAC3B"}
package/dist/types.d.ts CHANGED
@@ -19,6 +19,8 @@ export type ValidationIssue = Readonly<{
19
19
  export type FormValidator<TValues extends Record<string, unknown>> = (values: ReadonlyDeep<TValues>, signal: AbortSignal) => MaybePromise<readonly ValidationIssue[] | undefined>;
20
20
  export type FormOptions<TValues extends Record<string, unknown>> = Readonly<{
21
21
  initialValues: TValues;
22
+ /** Applied before immutable() at every write boundary (init, set, patch, reset, field.set). */
23
+ normalize?: (value: unknown) => unknown;
22
24
  onSubscriberError?: (error: unknown) => void;
23
25
  validate?: FormValidator<NoInfer<TValues>>;
24
26
  }>;
@@ -80,6 +82,8 @@ export type Form<TValues extends Record<string, unknown>> = {
80
82
  dispose(): void;
81
83
  readonly disposed: boolean;
82
84
  field<K extends keyof TValues & string>(key: K): Field<TValues[K]>;
85
+ patch(next: Partial<TValues>): void;
86
+ patch(updater: (previous: ReadonlyDeep<TValues>) => Partial<TValues>): void;
83
87
  reset(next?: TValues): void;
84
88
  set(next: TValues | ((previous: ReadonlyDeep<TValues>) => TValues)): void;
85
89
  readonly state: FormState;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vielzeug/forge",
3
- "version": "3.0.0",
4
- "description": "Immutable typed form state with focused fields and explicit full-form validation",
3
+ "version": "3.1.0",
4
+ "description": "Typed form state, validation, submission",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/helmuthdu/vielzeug",
@@ -82,8 +82,8 @@
82
82
  },
83
83
  "devDependencies": {
84
84
  "@types/node": "^26.4.1",
85
- "@vielzeug/spell": "3.0.0",
86
- "@vielzeug/vault": "3.0.0",
85
+ "@vielzeug/spell": "3.1.0",
86
+ "@vielzeug/vault": "3.0.1",
87
87
  "typescript": "^6.0.3",
88
88
  "vite": "^8.2.2",
89
89
  "vitest": "^5.0.0"