@vielzeug/forge 1.1.2
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/README.md +70 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +9 -0
- package/dist/_dev.js.map +1 -0
- package/dist/_utils.cjs +2 -0
- package/dist/_utils.cjs.map +1 -0
- package/dist/_utils.d.ts +12 -0
- package/dist/_utils.d.ts.map +1 -0
- package/dist/_utils.js +25 -0
- package/dist/_utils.js.map +1 -0
- package/dist/adapters/form-data.cjs +2 -0
- package/dist/adapters/form-data.cjs.map +1 -0
- package/dist/adapters/form-data.d.ts +8 -0
- package/dist/adapters/form-data.d.ts.map +1 -0
- package/dist/adapters/form-data.js +14 -0
- package/dist/adapters/form-data.js.map +1 -0
- package/dist/adapters/validators.cjs +2 -0
- package/dist/adapters/validators.cjs.map +1 -0
- package/dist/adapters/validators.d.ts +44 -0
- package/dist/adapters/validators.d.ts.map +1 -0
- package/dist/adapters/validators.js +20 -0
- package/dist/adapters/validators.js.map +1 -0
- package/dist/core/array.cjs +2 -0
- package/dist/core/array.cjs.map +1 -0
- package/dist/core/array.d.ts +10 -0
- package/dist/core/array.d.ts.map +1 -0
- package/dist/core/array.js +46 -0
- package/dist/core/array.js.map +1 -0
- package/dist/core/asyncQueue.cjs +2 -0
- package/dist/core/asyncQueue.cjs.map +1 -0
- package/dist/core/asyncQueue.d.ts +22 -0
- package/dist/core/asyncQueue.d.ts.map +1 -0
- package/dist/core/asyncQueue.js +67 -0
- package/dist/core/asyncQueue.js.map +1 -0
- package/dist/core/context.cjs +2 -0
- package/dist/core/context.cjs.map +1 -0
- package/dist/core/context.d.ts +10 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +176 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/lifecycle.cjs +2 -0
- package/dist/core/lifecycle.cjs.map +1 -0
- package/dist/core/lifecycle.d.ts +13 -0
- package/dist/core/lifecycle.d.ts.map +1 -0
- package/dist/core/lifecycle.js +42 -0
- package/dist/core/lifecycle.js.map +1 -0
- package/dist/core/observe.cjs +2 -0
- package/dist/core/observe.cjs.map +1 -0
- package/dist/core/observe.d.ts +18 -0
- package/dist/core/observe.d.ts.map +1 -0
- package/dist/core/observe.js +90 -0
- package/dist/core/observe.js.map +1 -0
- package/dist/core/scope.cjs +2 -0
- package/dist/core/scope.cjs.map +1 -0
- package/dist/core/scope.d.ts +8 -0
- package/dist/core/scope.d.ts.map +1 -0
- package/dist/core/scope.js +239 -0
- package/dist/core/scope.js.map +1 -0
- package/dist/core/validation.cjs +2 -0
- package/dist/core/validation.cjs.map +1 -0
- package/dist/core/validation.d.ts +23 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +173 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/core/values.cjs +2 -0
- package/dist/core/values.cjs.map +1 -0
- package/dist/core/values.d.ts +31 -0
- package/dist/core/values.d.ts.map +1 -0
- package/dist/core/values.js +149 -0
- package/dist/core/values.js.map +1 -0
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.ts +40 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +29 -0
- package/dist/devtools.js.map +1 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +22 -0
- package/dist/errors.js.map +1 -0
- package/dist/forge.cjs +2 -0
- package/dist/forge.cjs.map +1 -0
- package/dist/forge.iife.js +2 -0
- package/dist/forge.iife.js.map +1 -0
- package/dist/forge.js +2 -0
- package/dist/forge.js.map +1 -0
- package/dist/form.cjs +2 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.ts +6 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +89 -0
- package/dist/form.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/types.cjs +2 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +391 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forge.js","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/_dev.ts","../src/_utils.ts","../src/core/context.ts","../src/core/lifecycle.ts","../src/core/asyncQueue.ts","../src/core/observe.ts","../src/core/scope.ts","../src/core/validation.ts","../src/core/array.ts","../src/core/values.ts","../src/form.ts","../src/adapters/form-data.ts"],"sourcesContent":["/** Base class for all forge errors. Use `instanceof ForgeError` to catch any forge-originated error. */\nexport class ForgeError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is ForgeError {\n return err instanceof ForgeError;\n }\n}\n\n/** Thrown when any method is called on a disposed form. */\nexport class ForgeDisposedError extends ForgeError {\n constructor() {\n super('Cannot modify a disposed form');\n }\n}\n\n/** Thrown when a form key contains reserved prototype-polluting segments. */\nexport class ForgeConfigError extends ForgeError {}\n\n/** Thrown when `submit()` is called while a submission is already in progress. */\nexport class ForgeSubmitError extends ForgeError {}\n\n/** Thrown by `submitOrThrow()` when validation fails. */\nexport class ForgeValidationError extends ForgeError {\n readonly errors: Record<string, string>;\n\n constructor(errors: Record<string, string>) {\n super('Form validation failed');\n this.errors = errors;\n }\n}\n","/** A callback to remove a subscription. */\nexport type Unsubscribe = () => void;\n\nexport type MaybePromise<T> = T | PromiseLike<T>;\n\nexport type FieldValidator<V = unknown> = (value: V, signal?: AbortSignal) => MaybePromise<string | undefined>;\n\nexport type FormValidator<TValues extends Record<string, unknown> = Record<string, unknown>> = (\n values: TValues,\n signal?: AbortSignal,\n) => MaybePromise<Partial<Record<ErrorKeyOf<TValues>, string>> | undefined>;\n\nexport type SetOptions = {\n /** Whether to mark the field as touched. Default: false. */\n touched?: boolean;\n};\n\nexport type FormState = {\n errors: Readonly<Record<string, string> & { [FORM_ERROR]?: string }>;\n isDirty: boolean;\n /** `true` while an async `defaultValues` factory is resolving. */\n isLoading: boolean;\n isSubmitting: boolean;\n isTouched: boolean;\n isValid: boolean;\n isValidating: boolean;\n submitCount: number;\n /**\n * Field names that are currently touched, as full dot-notation paths.\n * On a scoped form these are still full paths (e.g. `\"address.city\"`, not `\"city\"`).\n * Prefer calling `scopedForm.validate()` to run scoped validation rather than\n * passing `state.touchedFields` directly — scoped `validate(fields[])` expects relative paths.\n */\n touchedFields: readonly string[];\n /** Field names that currently have an active async validation run. Useful for per-field loading indicators. */\n validatingFields: readonly string[];\n};\n\nexport type SubscribeOptions = {\n /** Call the listener immediately with the current state on subscription. Default: false. */\n sync?: boolean;\n};\n\n/** Shared field key for root-level / form-level validation errors. */\nexport const FORM_ERROR = '_form' as const;\n\nexport type ValidateResult = {\n errors: Readonly<Record<string, string>>;\n valid: boolean;\n};\n\nexport type SubmitResult<TResult = void> =\n { ok: true; value: TResult } | { errors: Record<string, string>; ok: false; type: 'validation' };\n\n/* -------------------- Utility Types -------------------- */\n\n/**\n * Recursively makes all properties optional.\n * Used by `form.patch()` to accept partial nested updates.\n */\nexport type DeepPartial<T> = T extends Record<string, unknown> ? { [K in keyof T]?: DeepPartial<T[K]> } : T;\n\n/**\n * Recursively extracts all dot-notation leaf keys from a values shape.\n * Depth-limited to 5: TypeScript compilation time grows non-linearly beyond this point.\n */\nexport type FlatKeyOf<\n T extends Record<string, unknown>,\n P extends string = '',\n D extends readonly 0[] = [],\n> = D['length'] extends 5\n ? string\n : {\n [K in keyof T & string]: T[K] extends unknown[] | File | Blob | Date\n ? P extends ''\n ? K\n : `${P}.${K}`\n : T[K] extends Record<string, unknown>\n ? FlatKeyOf<T[K] & Record<string, unknown>, P extends '' ? K : `${P}.${K}`, [...D, 0]>\n : P extends ''\n ? K\n : `${P}.${K}`;\n }[keyof T & string];\n\n/** Resolves the value type at a dot-notation path within a values shape. */\nexport type TypeAtPath<T, K extends string> = K extends `${infer Head}.${infer Tail}`\n ? Head extends keyof T\n ? T[Head] extends Record<string, unknown>\n ? TypeAtPath<T[Head], Tail>\n : unknown\n : unknown\n : K extends keyof T\n ? T[K]\n : unknown;\n\n/** Field error keys include field paths plus the reserved `_form` key for root-level issues. */\nexport type ErrorKeyOf<TValues extends Record<string, unknown>> = FlatKeyOf<TValues> | typeof FORM_ERROR;\n\n/**\n * Structural interface for safeParse-compatible schemas.\n * Works with `@vielzeug/spell`, Zod, Valibot, and any Standard Schema compliant library.\n */\nexport type SafeParseSchema = {\n safeParse(\n data: unknown,\n ): { success: true } | { error: { issues: { message: string; path: (string | number)[] }[] }; success: false };\n};\n\n/** The type of values accessible at a scoped sub-form prefix. */\nexport type ScopedValues<TValues, P extends string> =\n TypeAtPath<TValues, P> extends Record<string, unknown>\n ? TypeAtPath<TValues, P> & Record<string, unknown>\n : Record<string, unknown>;\n\n/**\n * The live connection object returned by `form.connect()`.\n * Getters re-evaluate on every property access — store the object once per field, do not destructure.\n * Call `dispose()` to cancel any pending debounce timers when the field unmounts.\n */\nexport type ConnectionResult<V = unknown> = {\n [Symbol.dispose](): void;\n readonly dirty: boolean;\n /** Cancels the debounce timer owned by this specific binding. Safe to call multiple times. */\n dispose(): void;\n /** `true` after `dispose()` has been called on this binding. */\n readonly disposed: boolean;\n readonly error: string | undefined;\n onBlur(): void;\n onChange(value: V): void;\n readonly touched: boolean;\n readonly value: V;\n};\n\n/** Per-field or global validation trigger configuration. */\nexport type ConnectOptions = {\n /**\n * Debounce auto-triggered validation by this many milliseconds.\n * Each `connect()` call owns its own timer — cancelling one binding does not affect others.\n * Default: 0.\n */\n debounce?: number;\n /** Auto-mark the field as touched when it loses focus. Default: false. */\n touchOnBlur?: boolean;\n /** Validate the field automatically when it loses focus. Default: false. */\n validateOnBlur?: boolean;\n /** Validate the field automatically after each value change. Default: false. */\n validateOnChange?: boolean;\n /** Validate on change only after the field has been touched at least once. */\n validateOnTouch?: boolean;\n};\n\n// Resolves the element type of an array, or `unknown` for non-arrays.\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : unknown;\n\nexport type ArrayField<T = unknown> = {\n append(value: T): void;\n insert(index: number, value: T): void;\n move(from: number, to: number): void;\n prepend(value: T): void;\n remove(index: number): void;\n replace(index: number, value: T): void;\n swap(a: number, b: number): void;\n};\n\nexport type FieldState<V = unknown> = {\n dirty: boolean;\n error: string | undefined;\n /** `true` when `error` is not `undefined`. Convenience alias for `error !== undefined`. */\n hasError: boolean;\n touched: boolean;\n value: V;\n};\n\n/**\n * Options for registering a dynamic field. Both properties are optional;\n * omitting `defaultValue` leaves the field uninitialized and omitting `validator`\n * skips field-level validation.\n */\nexport type RegisterFieldOptions<V = unknown> = {\n /** Initial value. Also becomes the baseline, so the field starts clean. */\n defaultValue?: V;\n /** Field-level validator to attach. */\n validator?: FieldValidator<V>;\n};\n\n/**\n * A complete snapshot of form state that can be restored via `form.restore()`.\n * `store` and `baseline` keys are the flattened dot-notation paths used internally.\n * Typed with `TValues` for nominal clarity — structurally both are `Record<string, unknown>`.\n */\nexport type FormSnapshot<TValues extends Record<string, unknown> = Record<string, unknown>> = {\n readonly baseline: Partial<Record<FlatKeyOf<TValues>, unknown>>;\n readonly dirty: readonly string[];\n readonly errors: Readonly<Record<string, string>>;\n readonly store: Partial<Record<FlatKeyOf<TValues>, unknown>>;\n readonly submitCount: number;\n readonly touched: readonly string[];\n};\n\nexport type FormOptions<TValues extends Record<string, unknown> = Record<string, unknown>> = {\n /**\n * Default connect behavior applied when `connect(name, config)` omits specific options.\n * Use `ValidationModes` presets for common strategies:\n * - `ValidationModes.onSubmit` (default) — validates only on submit, no touch-on-blur\n * - `ValidationModes.onBlur` — validates each field on blur\n * - `ValidationModes.onChange` — validates each field on every change\n * - `ValidationModes.onTouched` — validates on blur first, then on every change once touched\n */\n connect?: ConnectOptions;\n /**\n * Initial form values. May be a static object or an async factory function.\n * While a factory is pending, `form.isLoading` is `true` and the form is read-only.\n */\n defaultValues?: { [K in keyof TValues]: TValues[K] } | (() => Promise<{ [K in keyof TValues]: TValues[K] }>);\n /**\n * Called when an async `defaultValues` factory rejects.\n * Use to show an error UI or retry logic. In development, a warning is also emitted via `console.warn`.\n */\n onLoadError?: (error: unknown) => void;\n /**\n * Form-level validator. Accepts either:\n * - A `FormValidator` function\n * - Any `safeParse`-compatible schema (auto-detected, works with `@vielzeug/spell`, Zod, Valibot)\n */\n validator?: FormValidator<TValues> | SafeParseSchema;\n /** Field-level validators keyed by field name or dot-notation path. */\n validators?: Partial<Record<FlatKeyOf<TValues>, FieldValidator>>;\n};\n\n/**\n * Named presets for common validation trigger strategies.\n * Pass to `createForm({ connect: ValidationModes.onBlur })`.\n *\n * - `onSubmit` — no automatic validation or touch. Clean until the user submits.\n * - `onBlur` — validates and marks touched on blur.\n * - `onChange` — validates on every keystroke (marks touched on blur).\n * - `onTouched` — validates on blur first, then on every change once touched.\n */\nexport const ValidationModes = {\n onBlur: { touchOnBlur: true, validateOnBlur: true },\n onChange: { touchOnBlur: true, validateOnChange: true },\n onSubmit: {},\n onTouched: { touchOnBlur: true, validateOnBlur: true, validateOnTouch: true },\n} as const satisfies Record<string, ConnectOptions>;\n\nexport interface Form<TValues extends Record<string, unknown> = Record<string, unknown>> {\n /** Returns a cached typed `ArrayField` helper for mutating array-valued fields. */\n array<K extends FlatKeyOf<TValues>>(name: K): ArrayField<ElementOf<TypeAtPath<TValues, K>>>;\n batch(fn: () => void): void;\n /** Clear a single field error. No-op if the field has no error. */\n clearError(name: ErrorKeyOf<TValues>): void;\n /**\n * Returns a live connection object for a field (DOM event handlers + live getters).\n * Each call to `connect()` creates a new object with its own debounce timer.\n * Call `binding.dispose()` on unmount to cancel any pending debounce timer.\n * Do not destructure: getters re-evaluate on every property access.\n */\n connect<K extends FlatKeyOf<TValues>>(name: K, config?: ConnectOptions): ConnectionResult<TypeAtPath<TValues, K>>;\n dispose(): void;\n /** Aborted when `dispose()` is called. Use to cancel async work tied to this form's lifetime. */\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n field<K extends FlatKeyOf<TValues>>(name: K): FieldState<TypeAtPath<TValues, K>>;\n get<K extends FlatKeyOf<TValues>>(name: K): TypeAtPath<TValues, K>;\n /** `true` while an async `defaultValues` factory is resolving. Mirrors `state.isLoading`. */\n readonly isLoading: boolean;\n /** `true` while a `submit()` call is in progress. Mirrors `state.isSubmitting`. */\n readonly isSubmitting: boolean;\n /**\n * Merge a partial server response into the form: updates both the store and the baseline\n * for each provided field, marking them clean. Fields not included in `partial` are untouched.\n */\n patch(partial: DeepPartial<TValues>): void;\n /**\n * Field-lifecycle namespace — use to dynamically register/remove fields and manage validators.\n *\n * @example\n * const unsub = form.fields.register('email', { defaultValue: '', validator: required });\n * form.fields.setValidator('email', (v) => v ? undefined : 'Required');\n * form.fields.remove('email');\n */\n fields: {\n /**\n * Enumerate all currently-known field paths: the deduplicated union of populated store\n * keys and keys with a registered validator (the same definition `touchAll()` uses).\n * On a scoped form, paths are relative to the scope's prefix — matching `scope.state`'s\n * relative-path convention, not `state.touchedFields`'s absolute-path convention.\n */\n list(): readonly string[];\n /**\n * Declare a dynamic field with an optional default value and validator.\n * If the field does not yet exist in the store, `defaultValue` is written to both the\n * store and the baseline so the field starts clean (not dirty).\n * Returns an **unregister** callback that calls `fields.remove()` on the same field.\n */\n register<K extends FlatKeyOf<TValues>>(\n name: K,\n options?: RegisterFieldOptions<TypeAtPath<TValues, K>>,\n ): Unsubscribe;\n /** Remove a field entirely: drops value, dirty, touched, error, and any registered validator. */\n remove(name: FlatKeyOf<TValues>): void;\n /**\n * Add, replace, or remove a field validator.\n * Removing a validator (passing `undefined`) also immediately clears any existing error for that field.\n */\n setValidator(name: FlatKeyOf<TValues>, validator?: FieldValidator): void;\n };\n /** Replace values and baseline in one operation. Aborts any in-flight validation. */\n replace(newValues: TValues): void;\n /** Restore values from the current baseline and clear errors/touched/dirty. Aborts any in-flight validation. */\n reset(): void;\n resetField(name: FlatKeyOf<TValues>): void;\n /** Replace the entire error map. Use `undefined` values to omit entries. */\n resetErrors(errors?: Partial<Record<ErrorKeyOf<TValues>, string | undefined>>): void;\n /**\n * Returns a scoped sub-form whose field paths are relative to `prefix`.\n * `scopedForm.get('city')` is equivalent to `form.get('${prefix}.city')`.\n * The scoped form shares state and lifecycle with the parent — `dispose()` on a scoped\n * form is a no-op; call `parentForm.dispose()` to tear down the whole form.\n *\n * Results are memoized — calling `scope(prefix)` multiple times with the same prefix\n * returns the same object. Safe to call on every render.\n *\n * **`state`** returns a scoped projection: `errors`, `touchedFields`, `validatingFields`,\n * `isValid`, `isDirty`, and `isTouched` reflect only fields within this prefix.\n * `isSubmitting`, `isLoading`, `isValidating`, and `submitCount` reflect the full form.\n */\n scope<P extends FlatKeyOf<TValues>>(prefix: P): Form<ScopedValues<TValues, P>>;\n set<K extends FlatKeyOf<TValues>>(name: K, value: TypeAtPath<TValues, K>, options?: SetOptions): void;\n /** Set a field error. */\n setError(name: ErrorKeyOf<TValues>, message: string): void;\n /**\n * Capture the complete form state into a snapshot that can be passed to `restore()`.\n * Useful for undo/redo, draft saving, and \"discard changes\" flows.\n */\n snapshot(): FormSnapshot<TValues>;\n /**\n * Restore the form to a previously captured snapshot. Replaces all state:\n * values, baseline, errors, touched, dirty, and submitCount.\n * Aborts any in-flight validation.\n */\n restore(snapshot: FormSnapshot<TValues>): void;\n readonly state: FormState;\n submit<TResult = void>(handler: (values: TValues) => MaybePromise<TResult>): Promise<SubmitResult<TResult>>;\n /**\n * Like `submit()` but throws a `ForgeValidationError` when validation fails instead of returning\n * `{ ok: false, ... }`. Use when you prefer a throw-based control flow.\n *\n * @throws {ForgeValidationError} when validation fails.\n * @throws {Error} when `submit()` is already in progress.\n */\n submitOrThrow<TResult = void>(handler: (values: TValues) => MaybePromise<TResult>): Promise<TResult>;\n subscribe(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe;\n /**\n * Subscribe to form state changes filtered to this scope's prefix.\n * `errors`, `touchedFields`, and `validatingFields` are remapped to relative paths;\n * all other state flags reflect the full form.\n * The listener fires only when the scoped projection changes — unrelated field mutations\n * outside this prefix are suppressed.\n * On a root form, behaves identically to `subscribe` — no prefix filtering is applied.\n */\n subscribeScoped(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe;\n subscribeField<K extends FlatKeyOf<TValues>>(\n name: K,\n listener: (state: FieldState<TypeAtPath<TValues, K>>) => void,\n options?: SubscribeOptions,\n ): Unsubscribe;\n /**\n * Iterate over form state changes via `for await`.\n * Yields the current state immediately, then once per mutation until the iterator\n * is returned or the form is disposed.\n *\n * @example\n * for await (const state of form) {\n * if (state.isSubmitting) showSpinner();\n * }\n */\n [Symbol.asyncIterator](): AsyncIterableIterator<FormState>;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Mark a field as touched. */\n touch(name: FlatKeyOf<TValues>): void;\n /** Mark all known fields (store + validators) as touched. */\n touchAll(): void;\n /** Mark a field as untouched. No-op if the field is not currently touched. */\n untouch(name: FlatKeyOf<TValues>): void;\n /** Mark all fields as untouched. */\n untouchAll(): void;\n /**\n * Validate all registered field validators and the optional form validator.\n *\n * On a scoped form: validates only the fields within the scope and does not run the\n * form-level validator. Errors and `valid` reflect only scoped fields.\n */\n validate(signal?: AbortSignal): Promise<ValidateResult>;\n /**\n * Validate a single named field. Preserves other fields' errors. Does not run the form-level validator.\n * Returns a `ValidateResult` scoped to that field — `errors` contains at most one entry.\n */\n validate(name: FlatKeyOf<TValues>, signal?: AbortSignal): Promise<ValidateResult>;\n /**\n * Validate a specific set of fields. Preserves other fields' errors. Does not run the form-level validator.\n * Returns a `ValidateResult` scoped to the requested fields.\n */\n validate(fields: FlatKeyOf<TValues>[], signal?: AbortSignal): Promise<ValidateResult>;\n /**\n * Streaming validation — yields each field result as soon as its validator resolves.\n * The final item in the stream is the form-level validator result (field: '_form'), if configured.\n *\n * @example\n * for await (const result of form.validateStream()) {\n * console.log(result.field, result.error ?? 'ok');\n * }\n */\n validateStream(signal?: AbortSignal): AsyncIterableIterator<{ error: string | undefined; field: string }>;\n values(): TValues;\n}\n","const isDev = !(globalThis as { __FORGE_PROD__?: boolean }).__FORGE_PROD__;\n\n/** @internal @security Messages may include user-supplied form field paths. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/forge] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import { ForgeConfigError } from './errors';\n\nexport { flattenPaths as flattenValues, isPlainObject, unflattenPaths as unflattenValues } from '@vielzeug/arsenal';\n\nconst UNSAFE = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** Returns `true` when a dot-notation path contains no prototype-polluting segments. */\nexport function isSafeKey(path: string): boolean {\n return path.split('.').every((segment) => !UNSAFE.has(segment));\n}\n\n/** R2: Single assertion helper — replaces 5 copy-pasted guard blocks. */\nexport function assertSafeKey(key: string): void {\n if (!isSafeKey(key))\n throw new ForgeConfigError(`Unsafe key '${key}': segments __proto__, constructor, and prototype are reserved.`);\n}\n\n/**\n * Strips control characters (which could otherwise inject terminal escape sequences into\n * `console.warn` output) and caps length before interpolating caller-controlled text into a\n * dev warning. Mirrors the sanitization already applied to field keys in `warnIfArrayItemKey`.\n */\nexport function sanitizeForLog(text: string, maxLength = 200): string {\n return text.replace(/\\p{C}/gu, '?').slice(0, maxLength);\n}\n\n/**\n * Merges multiple AbortSignals into one that aborts when any of them aborts.\n * Nullish values are silently ignored.\n * Returns `undefined` when no non-null signals are provided.\n *\n * @internal Inlined here — `anySignal` was removed from `@vielzeug/arsenal`.\n */\nexport function anySignal(...signals: ReadonlyArray<AbortSignal | null | undefined>): AbortSignal | undefined {\n const active = signals.filter((s): s is AbortSignal => s != null);\n\n if (active.length === 0) return undefined;\n\n if (active.length === 1) return active[0];\n\n return AbortSignal.any(active);\n}\n","import { batch as rippleBatch, signal, type Signal } from '@vielzeug/ripple';\n\nimport { assertSafeKey, flattenValues, isSafeKey } from '../_utils';\nimport { ForgeDisposedError } from '../errors';\nimport {\n type ArrayField,\n type ConnectOptions,\n type FieldState,\n type FieldValidator,\n FORM_ERROR,\n type Form,\n type FormOptions,\n type FormState,\n type FormValidator,\n type SafeParseSchema,\n} from '../types';\n\n/**\n * Resolves the `validator` option to a FormValidator.\n * Auto-detects SafeParseSchema objects (duck-typed via `safeParse`) and wraps them,\n * enabling direct use of @vielzeug/spell, Zod, Valibot schemas without an explicit wrapper call.\n */\nfunction resolveFormValidator<TValues extends Record<string, unknown>>(\n raw: FormValidator<TValues> | SafeParseSchema | undefined,\n): FormValidator<TValues> | undefined {\n if (!raw) return undefined;\n\n if (typeof raw === 'function') return raw;\n\n const schema = raw as SafeParseSchema;\n\n return (values, signal) => {\n if (signal?.aborted) return undefined;\n\n const result = schema.safeParse(values);\n\n if (result.success) return undefined;\n\n const errors: Partial<Record<string, string>> = {};\n\n for (const issue of result.error.issues) {\n const key = issue.path.join('.') || FORM_ERROR;\n\n // Security: `issue.path` is schema-controlled (untrusted duck-typed input, per\n // SafeParseSchema) — reject reserved segments before it can become an error key.\n if (!isSafeKey(key)) continue;\n\n if (!errors[key]) errors[key] = issue.message;\n }\n\n return errors as Partial<Record<string, string>>;\n };\n}\n\n/**\n * Shared mutable state + primitives passed to every createForm() sub-module factory\n * (`createValueOps`, `createValidationOps`, `createObserveOps`, `createLifecycleOps`).\n * Mirrors the `ScopeContext` pattern already established in `internal/scope.ts`.\n * @internal\n */\nexport type FormContext<TValues extends Record<string, unknown> = Record<string, unknown>> = {\n arrayCache: Map<string, ArrayField>;\n baseline: Map<string, unknown>;\n batch(fn: () => void): void;\n buildFieldState(name: string): FieldState<unknown>;\n cachedErrors: Readonly<Record<string, string>> | null;\n cachedValues: TValues | null;\n computeErrors(): Readonly<Record<string, string>>;\n computeState(): FormState;\n connectDefaults: ConnectOptions;\n dirty: Set<string>;\n disposeController: AbortController;\n disposed: boolean;\n ensureNotDisposed(): void;\n fieldCtrls: Map<string, AbortController>;\n fieldErrors: Map<string, string>;\n fieldSignals: Map<string, Signal<FieldState<unknown>>>;\n fieldStateCache: Map<string, FieldState<unknown>>;\n formStateSignal: Signal<FormState>;\n formValidator: FormValidator<TValues> | undefined;\n getFieldSnapshot(name: string): FieldState<unknown>;\n getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>>;\n getStateSnapshot(): FormState;\n invalidateErrors(): void;\n invalidateValues(): void;\n isSubmittingState: boolean;\n loadingState: boolean;\n requestNotify(target?: string | Iterable<string>): void;\n rippleSubs: Set<{ dispose(): void }>;\n runCtrls: Set<AbortController>;\n scopeCache: Map<string, Form<never>>;\n store: Map<string, unknown>;\n submitCount: number;\n touched: Set<string>;\n validatingRuns: Map<string, Set<symbol>>;\n validators: Map<string, FieldValidator<unknown>>;\n};\n\n/**\n * Builds the raw mutable state + shared primitives for a form. This is the \"Core state\" +\n * \"Notification helpers\" sections of the original monolithic `createForm()` body, factored\n * out so `internal/values.ts`, `internal/validation.ts`, `internal/observe.ts`, and\n * `internal/lifecycle.ts` can each operate on the same shared state without closing over\n * ad-hoc free variables.\n */\nexport function createFormContext<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init: FormOptions<TValues>,\n): FormContext<TValues> {\n /* ---- Validators ---- */\n\n const validators = new Map<string, FieldValidator<unknown>>();\n\n for (const [name, validator] of Object.entries(init.validators ?? {})) {\n assertSafeKey(name);\n validators.set(name, validator as FieldValidator<unknown>);\n }\n\n const formValidator: FormValidator<TValues> | undefined = resolveFormValidator(init.validator);\n const connectDefaults: ConnectOptions = init.connect ?? {};\n\n /* ---- Core state ---- */\n\n let loadingState = typeof init.defaultValues === 'function';\n\n const staticValues = typeof init.defaultValues !== 'function' ? init.defaultValues : undefined;\n const baseline = new Map<string, unknown>(Object.entries(flattenValues(staticValues ?? {})));\n const store = new Map<string, unknown>(baseline);\n const fieldErrors = new Map<string, string>();\n const touched = new Set<string>();\n const dirty = new Set<string>();\n\n /* ---- Validation tracking ---- */\n\n /**\n * Symbol-based ref-counting for concurrent validation runs per field.\n * Each runValidationCore() call mints a unique symbol and adds it to the field's Set.\n * The field leaves `validatingFields` only when its Set is empty — impossible to miscount.\n */\n const validatingRuns = new Map<string, Set<symbol>>();\n const runCtrls = new Set<AbortController>();\n const fieldCtrls = new Map<string, AbortController>();\n const disposeController = new AbortController();\n\n /* ---- Caches ---- */\n\n let cachedValues: TValues | null = null;\n let cachedErrors: Readonly<Record<string, string>> | null = null;\n const fieldStateCache = new Map<string, FieldState<unknown>>();\n const arrayCache = new Map<string, ArrayField>();\n const scopeCache = new Map<string, Form<never>>();\n\n /* ---- Submission state ---- */\n\n let isSubmittingState = false;\n let submitCount = 0;\n\n /* ---- Lifecycle ---- */\n\n let disposed = false;\n\n /* ---- Reactive signals ---- */\n\n const fieldSignals = new Map<string, Signal<FieldState<unknown>>>();\n const rippleSubs = new Set<{ dispose(): void }>();\n\n function getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>> {\n if (!fieldSignals.has(key)) {\n fieldSignals.set(\n key,\n signal<FieldState<unknown>>(buildFieldState(key), {\n equals: (a, b) =>\n a.value === b.value &&\n a.error === b.error &&\n a.hasError === b.hasError &&\n a.touched === b.touched &&\n a.dirty === b.dirty,\n }),\n );\n }\n\n return fieldSignals.get(key)!;\n }\n\n /* ======== Notification helpers ======== */\n\n function invalidateValues(): void {\n cachedValues = null;\n }\n\n function invalidateErrors(): void {\n cachedErrors = null;\n }\n\n function computeErrors(): Readonly<Record<string, string>> {\n return (cachedErrors ??= Object.freeze(Object.fromEntries(fieldErrors)));\n }\n\n function computeState(): FormState {\n return Object.freeze({\n errors: computeErrors(),\n isDirty: dirty.size > 0,\n isLoading: loadingState,\n isSubmitting: isSubmittingState,\n isTouched: touched.size > 0,\n isValid: fieldErrors.size === 0,\n isValidating: validatingRuns.size > 0,\n submitCount,\n touchedFields: Object.freeze([...touched]) as readonly string[],\n validatingFields: Object.freeze([...validatingRuns.keys()]) as readonly string[],\n });\n }\n\n const formStateSignal = signal<FormState>(computeState());\n\n function getStateSnapshot(): FormState {\n return formStateSignal.value;\n }\n\n function buildFieldState(name: string): FieldState<unknown> {\n const error = fieldErrors.get(name);\n\n return {\n dirty: dirty.has(name),\n error,\n hasError: error !== undefined,\n touched: touched.has(name),\n value: store.get(name),\n };\n }\n\n function getFieldSnapshot(name: string): FieldState<unknown> {\n const cached = fieldStateCache.get(name);\n\n if (cached) return cached;\n\n const snap = Object.freeze(buildFieldState(name));\n\n fieldStateCache.set(name, snap);\n\n return snap;\n }\n\n /**\n * R4: Unified notification — replaces requestNotify(field?) + requestNotifyFields(fields).\n * - undefined → full-form refresh (all field signals + form signal)\n * - string → single field + form signal\n * - Iterable → targeted set of fields + form signal\n * rippleBatch deduplicates signal writes within the synchronous call stack.\n */\n function requestNotify(target?: string | Iterable<string>): void {\n if (disposed) return;\n\n invalidateValues();\n cachedErrors = null;\n\n if (target === undefined) {\n fieldStateCache.clear();\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n for (const [name, sig] of fieldSignals) sig.value = buildFieldState(name);\n });\n } else if (typeof target === 'string') {\n fieldStateCache.delete(target);\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n const sig = fieldSignals.get(target);\n\n if (sig) sig.value = buildFieldState(target);\n });\n } else {\n // Materialize before rippleBatch so the iterable isn't consumed inside the callback.\n const fields = [...target];\n\n for (const field of fields) fieldStateCache.delete(field);\n\n rippleBatch(() => {\n formStateSignal.value = computeState();\n\n for (const field of fields) {\n const sig = fieldSignals.get(field);\n\n if (sig) sig.value = buildFieldState(field);\n }\n });\n }\n }\n\n /* ======== Lifecycle guards ======== */\n\n function ensureNotDisposed(): void {\n if (disposed) throw new ForgeDisposedError();\n }\n\n /* ======== Batch ======== */\n\n function batch(fn: () => void): void {\n ensureNotDisposed();\n\n try {\n rippleBatch(fn);\n } catch (e) {\n // rippleBatch clears its pending-subscriber queue on error to prevent\n // stale flushes on future unrelated writes. Re-notify here so subscribers\n // still see the partial mutations that succeeded before the throw.\n requestNotify();\n throw e;\n }\n }\n\n return {\n arrayCache,\n baseline,\n batch,\n buildFieldState,\n get cachedErrors() {\n return cachedErrors;\n },\n set cachedErrors(value) {\n cachedErrors = value;\n },\n get cachedValues() {\n return cachedValues;\n },\n set cachedValues(value) {\n cachedValues = value;\n },\n computeErrors,\n computeState,\n connectDefaults,\n dirty,\n disposeController,\n get disposed() {\n return disposed;\n },\n set disposed(value) {\n disposed = value;\n },\n ensureNotDisposed,\n fieldCtrls,\n fieldErrors,\n fieldSignals,\n fieldStateCache,\n formStateSignal,\n formValidator,\n getFieldSnapshot,\n getOrCreateFieldSignal,\n getStateSnapshot,\n invalidateErrors,\n invalidateValues,\n get isSubmittingState() {\n return isSubmittingState;\n },\n set isSubmittingState(value) {\n isSubmittingState = value;\n },\n get loadingState() {\n return loadingState;\n },\n set loadingState(value) {\n loadingState = value;\n },\n requestNotify,\n rippleSubs,\n runCtrls,\n scopeCache,\n store,\n get submitCount() {\n return submitCount;\n },\n set submitCount(value) {\n submitCount = value;\n },\n touched,\n validatingRuns,\n validators,\n };\n}\n","import type { FormSnapshot } from '../types';\nimport type { FormContext } from './context';\n\nimport { isSafeKey } from '../_utils';\n\n/**\n * Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s\n * \"Lifecycle: dispose\" and \"Snapshot / Restore\" sections.\n */\nexport function createLifecycleOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>) {\n function dispose(): void {\n ctx.disposed = true;\n ctx.disposeController.abort();\n\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.fieldCtrls.clear();\n ctx.runCtrls.clear();\n ctx.arrayCache.clear();\n ctx.scopeCache.clear();\n\n for (const sub of ctx.rippleSubs) sub.dispose();\n\n ctx.rippleSubs.clear();\n ctx.fieldSignals.clear();\n }\n\n function snapshot(): FormSnapshot<TValues> {\n ctx.ensureNotDisposed();\n\n // Cast is irreducible: baseline/store are typed Partial<Record<FlatKeyOf<TValues>, unknown>>, a\n // TValues-dependent mapped type TS cannot verify a plain Object.fromEntries() result against for\n // a generic TValues param -- no runtime behavior is affected, only static verification.\n return Object.freeze({\n baseline: Object.freeze(Object.fromEntries(ctx.baseline)),\n dirty: Object.freeze([...ctx.dirty]),\n errors: Object.freeze(Object.fromEntries(ctx.fieldErrors)),\n store: Object.freeze(Object.fromEntries(ctx.store)),\n submitCount: ctx.submitCount,\n touched: Object.freeze([...ctx.touched]),\n }) as FormSnapshot<TValues>;\n }\n\n function restore(snap: FormSnapshot<TValues>): void {\n ctx.ensureNotDisposed();\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.fieldCtrls.clear();\n ctx.validatingRuns.clear();\n ctx.store.clear();\n ctx.baseline.clear();\n ctx.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n\n // Security: snap may be an externally-sourced blob (e.g. JSON.parse'd persisted state).\n // Unlike patch()/replace(), restore() writes flat keys directly into the internal Maps —\n // it must apply the same silent-drop defense-in-depth as flattenValues() does for those.\n for (const [k, v] of Object.entries(snap.store)) if (isSafeKey(k)) ctx.store.set(k, v);\n for (const [k, v] of Object.entries(snap.baseline)) if (isSafeKey(k)) ctx.baseline.set(k, v);\n for (const [k, v] of Object.entries(snap.errors)) if (isSafeKey(k)) ctx.fieldErrors.set(k, v);\n for (const t of snap.touched) if (isSafeKey(t)) ctx.touched.add(t);\n for (const d of snap.dirty) if (isSafeKey(d)) ctx.dirty.add(d);\n\n ctx.submitCount = snap.submitCount;\n\n ctx.invalidateErrors();\n ctx.invalidateValues();\n ctx.requestNotify();\n }\n\n return { dispose, restore, snapshot };\n}\n\nexport type LifecycleOps<TValues extends Record<string, unknown>> = ReturnType<typeof createLifecycleOps<TValues>>;\n","/**\n * A minimal push-based async queue exposing an `AsyncIterableIterator`.\n * Shared by `validateStream()` and `form[Symbol.asyncIterator]()`, which otherwise hand-roll\n * near-identical `queue`/`waitingResolve`/`done` plumbing.\n *\n * - `push()` delivers an item to a waiting `next()` call, or buffers it for the next one.\n * - `finish()` marks the queue complete; any items already buffered are still drained by\n * subsequent `next()` calls before `{ done: true }` is returned.\n * - `fail()` marks the queue complete and rejects the next `next()` call with `err`.\n * - Calling `iterator.return()` (e.g. via `for-await...of` `break`) hard-terminates the queue:\n * any buffered-but-undelivered items are discarded and `onTerminate` fires.\n */\nexport type AsyncQueue<T> = {\n fail(err: unknown): void;\n finish(): void;\n iterator: AsyncIterableIterator<T>;\n push(item: T): void;\n /** Hard-terminates the queue: drops any buffered items, marks it done, and fires `onTerminate`. */\n terminate(): void;\n};\n\nexport function createAsyncQueue<T>(onTerminate?: () => void): AsyncQueue<T> {\n type Resolver = (result: IteratorResult<T, undefined>) => void;\n type Rejecter = (err: unknown) => void;\n\n let buffer: T[] = [];\n let waitingResolve: Resolver | null = null;\n let waitingReject: Rejecter | null = null;\n let done = false;\n let pendingError: { err: unknown } | null = null;\n\n function push(item: T): void {\n if (done) return;\n\n if (waitingResolve) {\n const resolve = waitingResolve;\n\n waitingResolve = null;\n waitingReject = null;\n resolve({ done: false, value: item });\n } else {\n buffer.push(item);\n }\n }\n\n function fail(err: unknown): void {\n if (done) return;\n\n done = true;\n\n if (waitingReject) {\n const reject = waitingReject;\n\n waitingResolve = null;\n waitingReject = null;\n reject(err);\n } else {\n pendingError = { err };\n }\n }\n\n function finish(): void {\n if (done) return;\n\n done = true;\n\n if (waitingResolve) {\n const resolve = waitingResolve;\n\n waitingResolve = null;\n waitingReject = null;\n resolve({ done: true, value: undefined });\n }\n }\n\n function terminate(): void {\n if (done) return;\n\n buffer = [];\n finish();\n onTerminate?.();\n }\n\n const iterator: AsyncIterableIterator<T> = {\n next(): Promise<IteratorResult<T, undefined>> {\n if (pendingError) {\n const { err } = pendingError;\n\n pendingError = null;\n\n return Promise.reject(err);\n }\n\n if (buffer.length > 0) return Promise.resolve({ done: false, value: buffer.shift()! });\n\n if (done) return Promise.resolve({ done: true, value: undefined });\n\n return new Promise<IteratorResult<T, undefined>>((resolve, reject) => {\n waitingResolve = resolve;\n waitingReject = reject;\n });\n },\n\n return(): Promise<IteratorResult<T, undefined>> {\n terminate();\n\n return Promise.resolve({ done: true, value: undefined });\n },\n\n [Symbol.asyncIterator]() {\n return this;\n },\n };\n\n return { fail, finish, iterator, push, terminate };\n}\n","import { isAbortError } from '@vielzeug/arsenal';\nimport { watch } from '@vielzeug/ripple';\n\nimport type { FormContext } from './context';\nimport type { ValidationOps } from './validation';\nimport type { ValueOps } from './values';\n\nimport { assertSafeKey } from '../_utils';\nimport {\n type ConnectionResult,\n type ConnectOptions,\n type FieldState,\n type FlatKeyOf,\n type FormState,\n type SubscribeOptions,\n type TypeAtPath,\n type Unsubscribe,\n} from '../types';\nimport { createAsyncQueue } from './asyncQueue';\n\ntype ObserveDeps<TValues extends Record<string, unknown>> = Pick<ValueOps<TValues>, 'set' | 'touch'> &\n Pick<ValidationOps<TValues>, 'validateFields'>;\n\n/**\n * Observation operations: `subscribe`/`subscribeField`, `connect` (per-binding debounce timer),\n * and the `[Symbol.asyncIterator]` implementation. Moved verbatim from `createForm()`'s\n * \"Subscriptions\", \"Connect\", and \"Async iterator\" sections.\n */\nexport function createObserveOps<TValues extends Record<string, unknown>>(\n ctx: FormContext<TValues>,\n deps: ObserveDeps<TValues>,\n) {\n function subscribe(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe {\n if (ctx.disposed) return () => {};\n\n // Wrap listener so it always returns void — ripple watch throws on non-function returns.\n const sub = watch(ctx.formStateSignal, (state) => {\n listener(state);\n });\n\n ctx.rippleSubs.add(sub);\n\n if (options?.sync) listener(ctx.formStateSignal.value);\n\n return () => {\n sub.dispose();\n ctx.rippleSubs.delete(sub);\n };\n }\n\n function subscribeField<K extends FlatKeyOf<TValues>>(\n name: K,\n listener: (state: FieldState<TypeAtPath<TValues, K>>) => void,\n options?: SubscribeOptions,\n ): Unsubscribe {\n if (ctx.disposed) return () => {};\n\n const key = name as string;\n\n assertSafeKey(key);\n\n const sig = ctx.getOrCreateFieldSignal(key);\n // Wrap listener so it always returns void — ripple watch throws on non-function returns.\n const sub = watch(sig, (state) => {\n (listener as (state: FieldState<unknown>) => void)(state);\n });\n\n ctx.rippleSubs.add(sub);\n\n if (options?.sync) listener(sig.value as FieldState<TypeAtPath<TValues, K>>);\n\n return () => {\n sub.dispose();\n ctx.rippleSubs.delete(sub);\n };\n }\n\n /* ======== R5: Connect — per-binding debounce timer ======== */\n\n function connect<K extends FlatKeyOf<TValues>>(\n name: K,\n config?: ConnectOptions,\n ): ConnectionResult<TypeAtPath<TValues, K>> {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n\n const touchOnBlur = config?.touchOnBlur ?? ctx.connectDefaults.touchOnBlur ?? false;\n const validateOnBlur = config?.validateOnBlur ?? ctx.connectDefaults.validateOnBlur ?? false;\n const validateOnChange = config?.validateOnChange ?? ctx.connectDefaults.validateOnChange ?? false;\n const validateOnTouch = config?.validateOnTouch ?? ctx.connectDefaults.validateOnTouch ?? false;\n const debounceMs = config?.debounce ?? ctx.connectDefaults.debounce ?? 0;\n\n // R5: each connect() call owns its own timer — cancelling one binding never affects another.\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleValidation(): void {\n if (debounceMs > 0) {\n if (debounceTimer !== null) clearTimeout(debounceTimer);\n\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n\n if (ctx.disposed) return;\n\n void deps.validateFields([name]).catch((err: unknown) => {\n if (!isAbortError(err)) throw err;\n });\n }, debounceMs);\n } else {\n void deps.validateFields([name]).catch((err: unknown) => {\n if (!isAbortError(err)) throw err;\n });\n }\n }\n\n let bindingDisposed = false;\n\n return {\n get dirty() {\n return ctx.dirty.has(key);\n },\n /** Cancels this binding's own debounce timer. Does not affect other bindings for the same field. */\n dispose() {\n bindingDisposed = true;\n\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n },\n get disposed() {\n return bindingDisposed;\n },\n get error() {\n return ctx.fieldErrors.get(key);\n },\n onBlur: () => {\n if (ctx.disposed) return;\n\n if (touchOnBlur) deps.touch(name);\n\n if (validateOnBlur) scheduleValidation();\n },\n onChange: (value: TypeAtPath<TValues, K>) => {\n if (ctx.disposed) return;\n\n deps.set(name, value);\n\n const shouldValidate = validateOnChange || (validateOnTouch && ctx.touched.has(key));\n\n if (shouldValidate) scheduleValidation();\n },\n [Symbol.dispose]() {\n this.dispose();\n },\n get touched() {\n return ctx.touched.has(key);\n },\n get value() {\n return ctx.store.get(key) as TypeAtPath<TValues, K>;\n },\n };\n }\n\n /* ======== Async iterator (for await...of form) ======== */\n\n function createAsyncIterator(): AsyncIterableIterator<FormState> {\n const queue = createAsyncQueue<FormState>(() => unsubscribe());\n\n queue.push(ctx.getStateSnapshot());\n\n const unsubscribe = subscribe((state) => {\n queue.push(state);\n });\n\n ctx.disposeController.signal.addEventListener('abort', () => queue.terminate(), { once: true });\n\n return queue.iterator;\n }\n\n return { connect, subscribe, subscribeField, [Symbol.asyncIterator]: createAsyncIterator };\n}\n","import type {\n ArrayField,\n ConnectionResult,\n ErrorKeyOf,\n FieldState,\n FieldValidator,\n FlatKeyOf,\n Form,\n FormSnapshot,\n FormState,\n MaybePromise,\n RegisterFieldOptions,\n ScopedValues,\n SetOptions,\n SubmitResult,\n SubscribeOptions,\n TypeAtPath,\n Unsubscribe,\n ValidateResult,\n} from '../types';\n\nimport { flattenValues, unflattenValues } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\n\n/**\n * R3: Slim ScopeContext — delegates simple operations through `root: Form<TValues>`.\n * Only the Maps/Sets needed for bulk scoped operations are passed directly.\n * @internal\n */\nexport interface ScopeContext<TValues extends Record<string, unknown> = Record<string, unknown>> {\n /** The fully-featured root form — used for all simple one-field delegations. */\n root: Form<TValues>;\n\n /* ---- Shared mutable state (passed by reference, needed by bulk scoped ops) ---- */\n store: Map<string, unknown>;\n baseline: Map<string, unknown>;\n validators: Map<string, FieldValidator<unknown>>;\n fieldErrors: Map<string, string>;\n touched: Set<string>;\n dirty: Set<string>;\n fieldCtrls: Map<string, AbortController>;\n\n /* ---- Scalar state accessors ---- */\n isSubmitting(): boolean;\n setSubmitting(value: boolean): void;\n incrementSubmitCount(): void;\n isDisposed(): boolean;\n\n /* ---- Core helpers ---- */\n ensureNotDisposed(): void;\n invalidateErrors(): void;\n requestNotify(target?: string | Iterable<string>): void;\n getStateSnapshot(): FormState;\n\n /* ---- Validation core (needed for scoped validate/submit) ---- */\n runValidationCore(\n fields: string[],\n scope: 'full' | 'partial',\n signal?: AbortSignal,\n ): Promise<ValidateResult & { aborted: boolean }>;\n}\n\n/**\n * Creates a scoped sub-form whose field paths are relative to `pfx`.\n * All simple single-field operations delegate to `ctx.root`.\n * Only prefix-aware bulk operations (submit, validate, replace, reset, etc.) contain custom code.\n */\nexport function createScopedForm<TValues extends Record<string, unknown>, P extends string>(\n ctx: ScopeContext<TValues>,\n pfx: P,\n): Form<ScopedValues<TValues, P>> {\n const pfxDot = `${pfx}.`;\n\n function pre(name: string): string {\n return `${pfx}.${name}`;\n }\n\n function isScopedKey(key: string): boolean {\n return key === pfx || key.startsWith(pfxDot);\n }\n\n function unscope(key: string): string {\n return key.startsWith(pfxDot) ? key.slice(pfxDot.length) : key;\n }\n\n /* ---- Non-trivial scoped implementations ---- */\n\n /**\n * Compute a scoped projection of FormState from current raw Maps.\n * Errors, touchedFields, validatingFields, isValid, isDirty, isTouched reflect only\n * fields within the prefix. isSubmitting, isLoading, isValidating, submitCount are full-form.\n */\n function getScopedState(): FormState {\n const rootState = ctx.getStateSnapshot();\n const errors = Object.fromEntries(\n Object.entries(rootState.errors)\n .filter(([k]) => isScopedKey(k))\n .map(([k, v]) => [unscope(k), v]),\n );\n const touchedFields = rootState.touchedFields.filter(isScopedKey).map(unscope);\n const validatingFields = rootState.validatingFields.filter(isScopedKey).map(unscope);\n const isValid = Object.keys(errors).length === 0;\n const isDirty = [...ctx.dirty].some(isScopedKey);\n const isTouched = [...ctx.touched].some(isScopedKey);\n const isValidating = validatingFields.length > 0;\n\n return Object.freeze({\n errors: Object.freeze(errors) as FormState['errors'],\n isDirty,\n isLoading: rootState.isLoading,\n isSubmitting: rootState.isSubmitting,\n isTouched,\n isValid,\n isValidating,\n submitCount: rootState.submitCount,\n touchedFields: Object.freeze(touchedFields) as readonly string[],\n validatingFields: Object.freeze(validatingFields) as readonly string[],\n });\n }\n\n function scopedValues(): ScopedValues<TValues, P> {\n const sub: Record<string, unknown> = {};\n\n for (const [key, value] of ctx.store) {\n if (key.startsWith(pfxDot)) sub[key.slice(pfxDot.length)] = value;\n }\n\n return unflattenValues(sub) as ScopedValues<TValues, P>;\n }\n\n /**\n * Enumerate field paths within this scope's prefix, stripped to relative paths —\n * matching `scope.state`'s relative-path convention, not `state.touchedFields`'s\n * absolute-path convention. Same \"known field\" definition `scopedTouchAll()` uses.\n */\n function scopedListFields(): readonly string[] {\n const known = new Set<string>();\n\n for (const key of ctx.store.keys()) {\n if (isScopedKey(key)) known.add(unscope(key));\n }\n\n for (const key of ctx.validators.keys()) {\n if (isScopedKey(key)) known.add(unscope(key));\n }\n\n return Object.freeze([...known]);\n }\n\n function scopedTouchAll(): void {\n ctx.ensureNotDisposed();\n\n for (const name of ctx.store.keys()) {\n if (isScopedKey(name)) ctx.touched.add(name);\n }\n\n for (const name of ctx.validators.keys()) {\n if (isScopedKey(name)) ctx.touched.add(name);\n }\n\n ctx.requestNotify();\n }\n\n function scopedUntouchAll(): void {\n ctx.ensureNotDisposed();\n\n for (const name of [...ctx.touched]) {\n if (isScopedKey(name)) ctx.touched.delete(name);\n }\n\n ctx.requestNotify();\n }\n\n function scopedReset(): void {\n ctx.ensureNotDisposed();\n\n for (const key of [...ctx.store.keys()]) {\n if (!isScopedKey(key)) continue;\n\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n ctx.store.set(key, ctx.baseline.get(key));\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n }\n\n ctx.requestNotify();\n }\n\n function scopedReplace(newValues: ScopedValues<TValues, P>): void {\n ctx.ensureNotDisposed();\n\n for (const key of [...ctx.fieldCtrls.keys()]) {\n if (isScopedKey(key)) {\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n }\n }\n\n const flat = flattenValues(newValues as Record<string, unknown>);\n\n for (const key of [...ctx.store.keys()]) {\n if (!isScopedKey(key)) continue;\n\n ctx.store.delete(key);\n ctx.baseline.delete(key);\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n ctx.fieldErrors.delete(key);\n }\n\n ctx.invalidateErrors();\n\n for (const [key, value] of Object.entries(flat)) {\n const full = pre(key);\n\n ctx.store.set(full, value);\n ctx.baseline.set(full, value);\n }\n\n ctx.requestNotify();\n }\n\n function scopedPatch(partial: Record<string, unknown>): void {\n ctx.ensureNotDisposed();\n\n const flat = flattenValues(partial);\n const changedKeys: string[] = [];\n\n for (const [key, value] of Object.entries(flat)) {\n const full = pre(key);\n\n ctx.baseline.set(full, value);\n ctx.store.set(full, value);\n ctx.dirty.delete(full);\n changedKeys.push(full);\n }\n\n ctx.requestNotify(changedKeys);\n }\n\n function scopedResetErrors(\n nextErrors?: Partial<Record<ErrorKeyOf<ScopedValues<TValues, P>>, string | undefined>>,\n ): void {\n ctx.ensureNotDisposed();\n\n for (const key of [...ctx.fieldErrors.keys()]) {\n if (isScopedKey(key)) ctx.fieldErrors.delete(key);\n }\n\n if (nextErrors) {\n for (const [key, message] of Object.entries(nextErrors)) {\n if (typeof message === 'string') ctx.fieldErrors.set(pre(key), message);\n }\n }\n\n ctx.invalidateErrors();\n ctx.requestNotify();\n }\n\n async function scopedValidate(\n nameOrFieldsOrSignal?: FlatKeyOf<ScopedValues<TValues, P>> | FlatKeyOf<ScopedValues<TValues, P>>[] | AbortSignal,\n signal?: AbortSignal,\n ): Promise<ValidateResult> {\n ctx.ensureNotDisposed();\n\n if (\n nameOrFieldsOrSignal !== undefined &&\n !Array.isArray(nameOrFieldsOrSignal) &&\n !(nameOrFieldsOrSignal instanceof AbortSignal)\n ) {\n // validate(name) — single field\n const prefixedName = pre(nameOrFieldsOrSignal as string) as FlatKeyOf<TValues>;\n\n await ctx.runValidationCore([prefixedName as string], 'partial', signal);\n\n const error = ctx.fieldErrors.get(prefixedName as string);\n\n return {\n errors: error !== undefined ? { [nameOrFieldsOrSignal as string]: error } : {},\n valid: error === undefined,\n };\n }\n\n if (Array.isArray(nameOrFieldsOrSignal)) {\n // validate(fields[]) — specific subset\n const prefixedFields = nameOrFieldsOrSignal.map((f) => pre(f as string)) as string[];\n\n await ctx.runValidationCore(prefixedFields, 'partial', signal);\n\n const errors: Record<string, string> = {};\n\n for (const pf of prefixedFields) {\n const msg = ctx.fieldErrors.get(pf);\n\n if (msg !== undefined) errors[unscope(pf)] = msg;\n }\n\n return { errors, valid: Object.keys(errors).length === 0 };\n }\n\n // validate(signal?) — all scoped fields\n const sig = nameOrFieldsOrSignal as AbortSignal | undefined;\n const fields = [...ctx.validators.keys()].filter(isScopedKey);\n\n await ctx.runValidationCore(fields, 'partial', sig);\n\n const errors: Record<string, string> = {};\n\n for (const [key, message] of ctx.fieldErrors) {\n if (isScopedKey(key)) errors[unscope(key)] = message;\n }\n\n return { errors, valid: Object.keys(errors).length === 0 };\n }\n\n async function scopedSubmit<TResult>(\n handler: (values: ScopedValues<TValues, P>) => MaybePromise<TResult>,\n ): Promise<SubmitResult<TResult>> {\n ctx.ensureNotDisposed();\n\n if (ctx.isSubmitting()) throw new ForgeSubmitError('submit() called while a submission is already in progress');\n\n ctx.root.batch(() => {\n ctx.incrementSubmitCount();\n ctx.setSubmitting(true);\n scopedTouchAll();\n });\n\n try {\n const fields = [...ctx.validators.keys()].filter(isScopedKey);\n\n await ctx.runValidationCore(fields, 'partial');\n\n ctx.ensureNotDisposed();\n\n const errors: Record<string, string> = {};\n\n for (const [key, message] of ctx.fieldErrors) {\n if (isScopedKey(key)) errors[unscope(key)] = message;\n }\n\n if (Object.keys(errors).length > 0) {\n return { errors, ok: false, type: 'validation' as const };\n }\n\n return { ok: true as const, value: await handler(scopedValues()) };\n } finally {\n ctx.setSubmitting(false);\n\n if (!ctx.isDisposed()) ctx.requestNotify();\n }\n }\n\n /**\n * Subscribe filtered to scoped fields only.\n * Errors, touchedFields, and validatingFields are remapped to relative paths.\n * `isValid`, `isDirty`, and `isTouched` reflect only fields within this scope's prefix.\n * `isSubmitting`, `isLoading`, `isValidating`, and `submitCount` reflect the full form.\n *\n * The listener fires only when the scoped projection changes — mutations outside this\n * prefix do not fire the listener.\n */\n function subscribeScoped(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe {\n // Track the previous scoped projection for equality comparison.\n let prevState: FormState | null = null;\n\n return ctx.root.subscribe(() => {\n const next = getScopedState();\n\n // Skip if no scoped-relevant state has changed.\n if (prevState) {\n const p = prevState;\n const errKeys = Object.keys(next.errors);\n const prevErrKeys = Object.keys(p.errors);\n const errorsMatch =\n errKeys.length === prevErrKeys.length && errKeys.every((k) => next.errors[k] === p.errors[k]);\n\n const touchedMatch =\n next.touchedFields.length === p.touchedFields.length &&\n next.touchedFields.every((v, i) => v === p.touchedFields[i]);\n\n const validatingMatch =\n next.validatingFields.length === p.validatingFields.length &&\n next.validatingFields.every((v, i) => v === p.validatingFields[i]);\n\n const flagsMatch =\n next.isDirty === p.isDirty &&\n next.isLoading === p.isLoading &&\n next.isSubmitting === p.isSubmitting &&\n next.isTouched === p.isTouched &&\n next.isValid === p.isValid &&\n next.isValidating === p.isValidating &&\n next.submitCount === p.submitCount;\n\n if (flagsMatch && errorsMatch && touchedMatch && validatingMatch) return;\n }\n\n prevState = next;\n listener(next);\n }, options);\n }\n\n /* ---- Public scoped form object ---- */\n\n type S = ScopedValues<TValues, P>;\n\n return {\n array: (name) =>\n ctx.root.array(pre(name as string) as FlatKeyOf<TValues>) as ArrayField<\n TypeAtPath<S, typeof name> extends readonly (infer E)[] ? E : unknown\n >,\n batch: (fn) => ctx.root.batch(fn),\n clearError: (name) => ctx.root.clearError(pre(name as string) as ErrorKeyOf<TValues>),\n connect: (name, config?) =>\n ctx.root.connect(pre(name as string) as FlatKeyOf<TValues>, config) as ConnectionResult<\n TypeAtPath<S, typeof name>\n >,\n get disposalSignal() {\n return ctx.root.disposalSignal;\n },\n dispose: () => {\n /* Scoped forms share lifecycle with parent — call parentForm.dispose() to tear down */\n },\n get disposed() {\n return ctx.isDisposed();\n },\n field: (name) =>\n ctx.root.field(pre(name as string) as FlatKeyOf<TValues>) as FieldState<TypeAtPath<S, typeof name>>,\n fields: {\n list: scopedListFields,\n register: (name, options?) =>\n ctx.root.fields.register(\n pre(name as string) as FlatKeyOf<TValues>,\n options as RegisterFieldOptions<TypeAtPath<TValues, FlatKeyOf<TValues>>>,\n ),\n remove: (name) => ctx.root.fields.remove(pre(name as string) as FlatKeyOf<TValues>),\n setValidator: (name, validator?) =>\n ctx.root.fields.setValidator(pre(name as string) as FlatKeyOf<TValues>, validator),\n },\n get: (name) => ctx.root.get(pre(name as string) as FlatKeyOf<TValues>) as TypeAtPath<S, typeof name>,\n get isLoading() {\n return ctx.root.isLoading;\n },\n get isSubmitting() {\n return ctx.root.isSubmitting;\n },\n patch: scopedPatch as Form<S>['patch'],\n replace: scopedReplace as Form<S>['replace'],\n reset: scopedReset,\n resetErrors: scopedResetErrors as Form<S>['resetErrors'],\n resetField: (name) => ctx.root.resetField(pre(name as string) as FlatKeyOf<TValues>),\n // Cast is irreducible: FormSnapshot<ScopedValues<TValues,P>> and FormSnapshot<TValues> are\n // structurally unrelated generic instantiations -- TS cannot verify one against the other.\n restore: (snap) => ctx.root.restore(snap as FormSnapshot<TValues>),\n scope: (subPrefix) => createScopedForm(ctx, pre(subPrefix as string)) as Form<ScopedValues<S, typeof subPrefix>>,\n set: (name, value, options?: SetOptions) =>\n ctx.root.set(\n pre(name as string) as FlatKeyOf<TValues>,\n value as TypeAtPath<TValues, FlatKeyOf<TValues>>,\n options,\n ),\n setError: (name, message) => ctx.root.setError(pre(name as string) as ErrorKeyOf<TValues>, message),\n // Cast is irreducible: FormSnapshot<TValues> and FormSnapshot<ScopedValues<TValues,P>> are\n // structurally unrelated generic instantiations -- TS cannot verify one against the other.\n snapshot: () => ctx.root.snapshot() as FormSnapshot<S>,\n get state() {\n return getScopedState();\n },\n submit: scopedSubmit as Form<S>['submit'],\n submitOrThrow: async (handler) => {\n const result = await scopedSubmit(handler);\n\n if (!result.ok) throw new ForgeValidationError(result.errors as Record<string, string>);\n\n return result.value;\n },\n subscribe: (listener, options?) => ctx.root.subscribe(listener, options),\n subscribeField: (name, listener, options?) =>\n ctx.root.subscribeField(\n pre(name as string) as FlatKeyOf<TValues>,\n listener as (state: FieldState<TypeAtPath<TValues, FlatKeyOf<TValues>>>) => void,\n options,\n ),\n subscribeScoped,\n [Symbol.asyncIterator]: () => ctx.root[Symbol.asyncIterator](),\n [Symbol.dispose]() {\n this.dispose();\n },\n touch: (name) => ctx.root.touch(pre(name as string) as FlatKeyOf<TValues>),\n touchAll: scopedTouchAll,\n untouch: (name) => ctx.root.untouch(pre(name as string) as FlatKeyOf<TValues>),\n untouchAll: scopedUntouchAll,\n validate: scopedValidate as Form<S>['validate'],\n validateStream(signal?) {\n const rootIter = ctx.root.validateStream(signal);\n\n const iter: AsyncIterableIterator<{ error: string | undefined; field: string }> = {\n async next() {\n for (;;) {\n const item = await rootIter.next();\n\n if (item.done) return item;\n\n if (isScopedKey(item.value.field))\n return { done: false, value: { error: item.value.error, field: unscope(item.value.field) } };\n }\n },\n return() {\n return rootIter.return ? rootIter.return() : Promise.resolve({ done: true as const, value: undefined });\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n };\n\n return iter;\n },\n values: scopedValues,\n };\n}\n","import { isAbortError } from '@vielzeug/arsenal';\n\nimport type { FormContext } from './context';\nimport type { ValueOps } from './values';\n\nimport { anySignal, isSafeKey } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\nimport { type FlatKeyOf, FORM_ERROR, type MaybePromise, type SubmitResult, type ValidateResult } from '../types';\nimport { createAsyncQueue } from './asyncQueue';\n\n/**\n * Validation + submit operations: field/form validator execution, error application,\n * `validate()`/`validateFields()`/`validateStream()`, and `submit()`/`submitOrThrow()`.\n * Moved verbatim from `createForm()`'s \"Validation\" and \"Submit\" sections.\n */\nexport function createValidationOps<TValues extends Record<string, unknown>>(\n ctx: FormContext<TValues>,\n deps: Pick<ValueOps<TValues>, 'touchAll' | 'values'>,\n) {\n async function runFieldValidator(name: string, signal: AbortSignal): Promise<string | undefined> {\n const validator = ctx.validators.get(name);\n\n if (!validator) return undefined;\n\n if (signal.aborted) throw signal.reason;\n\n const result = await validator(ctx.store.get(name), signal);\n\n return typeof result === 'string' ? result : undefined;\n }\n\n async function runFormValidator(signal: AbortSignal): Promise<Record<string, string>> {\n if (!ctx.formValidator) return {};\n\n if (signal.aborted) throw signal.reason;\n\n const result = await ctx.formValidator(deps.values(), signal);\n const errors: Record<string, string> = {};\n\n if (result) {\n for (const [key, message] of Object.entries(result)) {\n // Security: a custom FormValidator function is caller-authored code — its returned\n // keys are just as untrusted as a schema's `issue.path`. Reject reserved segments.\n if (isSafeKey(key) && typeof message === 'string') errors[key] = message;\n }\n }\n\n return errors;\n }\n\n function applyFieldErrors(\n fieldSet: Set<string>,\n nextErrors: Record<string, string>,\n formErrors: Record<string, string>,\n scope: 'full' | 'partial',\n ): void {\n if (scope === 'full') {\n ctx.fieldErrors.clear();\n\n for (const [key, message] of Object.entries(formErrors)) {\n if (nextErrors[key] === undefined) nextErrors[key] = message;\n }\n\n for (const [key, message] of Object.entries(nextErrors)) ctx.fieldErrors.set(key, message);\n\n ctx.invalidateErrors();\n ctx.requestNotify();\n } else {\n const changedFields = new Set<string>();\n\n for (const name of fieldSet) {\n const next = nextErrors[name];\n const previous = ctx.fieldErrors.get(name);\n\n if (next !== undefined) {\n ctx.fieldErrors.set(name, next);\n\n if (previous !== next) changedFields.add(name);\n } else if (ctx.fieldErrors.delete(name)) {\n changedFields.add(name);\n }\n }\n\n if (changedFields.size > 0) ctx.invalidateErrors();\n\n ctx.requestNotify(changedFields);\n }\n }\n\n async function runValidationCore(\n fields: string[],\n scope: 'full' | 'partial',\n signal?: AbortSignal,\n ): Promise<ValidateResult & { aborted: boolean }> {\n const fieldSet = new Set(fields);\n const fieldRunCtrls = new Map<string, AbortController>();\n\n for (const name of fieldSet) {\n ctx.fieldCtrls.get(name)?.abort();\n\n const ctrl = new AbortController();\n\n ctx.fieldCtrls.set(name, ctrl);\n fieldRunCtrls.set(name, ctrl);\n }\n\n const runCtrl = new AbortController();\n const runSignal = anySignal(runCtrl.signal, signal, ctx.disposeController.signal)!;\n\n ctx.runCtrls.add(runCtrl);\n\n // Symbol-based ref-counting: each run gets a unique token per field.\n // The field leaves validatingFields only when its Set empties — impossible to miscount.\n const runId = Symbol();\n\n for (const name of fieldSet) {\n let runs = ctx.validatingRuns.get(name);\n\n if (!runs) {\n runs = new Set<symbol>();\n ctx.validatingRuns.set(name, runs);\n }\n\n runs.add(runId);\n }\n\n ctx.requestNotify();\n\n try {\n const results = await Promise.all(\n [...fieldSet].map(async (name) => {\n const fieldSignal = scope === 'partial' ? anySignal(fieldRunCtrls.get(name)!.signal, runSignal)! : runSignal;\n\n return [name, await runFieldValidator(name, fieldSignal)] as const;\n }),\n );\n\n if (runSignal.aborted) {\n return { aborted: true, errors: Object.fromEntries(ctx.fieldErrors), valid: ctx.fieldErrors.size === 0 };\n }\n\n const nextErrors: Record<string, string> = {};\n\n for (const [name, message] of results) {\n if (message !== undefined) nextErrors[name] = message;\n }\n\n const formErrors = scope === 'full' ? await runFormValidator(runSignal) : {};\n\n applyFieldErrors(fieldSet, nextErrors, formErrors, scope);\n\n return { aborted: false, errors: Object.fromEntries(ctx.fieldErrors), valid: ctx.fieldErrors.size === 0 };\n } catch (error) {\n if (isAbortError(error)) {\n return { aborted: true, errors: Object.fromEntries(ctx.fieldErrors), valid: ctx.fieldErrors.size === 0 };\n }\n\n throw error;\n } finally {\n ctx.runCtrls.delete(runCtrl);\n\n for (const name of fieldSet) {\n const runs = ctx.validatingRuns.get(name);\n\n if (runs) {\n runs.delete(runId);\n\n if (runs.size === 0) ctx.validatingRuns.delete(name);\n }\n }\n\n ctx.requestNotify();\n\n for (const [name, ctrl] of fieldRunCtrls) {\n if (ctx.fieldCtrls.get(name) === ctrl) ctx.fieldCtrls.delete(name);\n }\n }\n }\n\n async function validateFields(\n fields: FlatKeyOf<TValues>[] | string[],\n signal?: AbortSignal,\n ): Promise<ValidateResult> {\n ctx.ensureNotDisposed();\n\n const result = await runValidationCore(fields as string[], 'partial', signal);\n\n return { errors: result.errors, valid: result.valid };\n }\n\n async function validate(\n nameOrFieldsOrSignal?: FlatKeyOf<TValues> | FlatKeyOf<TValues>[] | AbortSignal,\n signal?: AbortSignal,\n ): Promise<ValidateResult> {\n ctx.ensureNotDisposed();\n\n if (nameOrFieldsOrSignal === undefined || nameOrFieldsOrSignal instanceof AbortSignal) {\n const result = await runValidationCore(\n [...ctx.validators.keys()],\n 'full',\n nameOrFieldsOrSignal as AbortSignal | undefined,\n );\n\n return { errors: result.errors, valid: result.valid };\n }\n\n if (Array.isArray(nameOrFieldsOrSignal)) {\n return validateFields(nameOrFieldsOrSignal, signal);\n }\n\n await validateFields([nameOrFieldsOrSignal], signal);\n\n const name = nameOrFieldsOrSignal as string;\n const error = ctx.fieldErrors.get(name);\n\n return { errors: error !== undefined ? { [name]: error } : {}, valid: error === undefined };\n }\n\n /* ======== Submit ======== */\n\n async function submitOrThrow<TResult = void>(handler: (values: TValues) => MaybePromise<TResult>): Promise<TResult> {\n const result = await submit(handler);\n\n if (!result.ok) throw new ForgeValidationError(result.errors as Record<string, string>);\n\n return result.value;\n }\n\n async function submit<TResult = void>(\n handler: (values: TValues) => MaybePromise<TResult>,\n ): Promise<SubmitResult<TResult>> {\n ctx.ensureNotDisposed();\n\n if (ctx.isSubmittingState) throw new ForgeSubmitError('submit() called while a submission is already in progress');\n\n ctx.batch(() => {\n ctx.submitCount++;\n ctx.isSubmittingState = true;\n deps.touchAll();\n });\n\n try {\n const validation = await runValidationCore([...ctx.validators.keys()], 'full');\n\n ctx.ensureNotDisposed();\n\n if (!validation.valid) {\n return { errors: validation.errors, ok: false, type: 'validation' };\n }\n\n return { ok: true, value: await handler(deps.values()) };\n } finally {\n ctx.isSubmittingState = false;\n\n if (!ctx.disposed) ctx.requestNotify();\n }\n }\n\n /* ======== validateStream ======== */\n\n function validateStream(signal?: AbortSignal): AsyncIterableIterator<{ error: string | undefined; field: string }> {\n ctx.ensureNotDisposed();\n\n const fields = [...ctx.validators.keys()];\n const ctrl = new AbortController();\n const combined = signal\n ? anySignal(ctrl.signal, signal, ctx.disposeController.signal)!\n : anySignal(ctrl.signal, ctx.disposeController.signal)!;\n\n type Item = { error: string | undefined; field: string };\n\n const queue = createAsyncQueue<Item>(() => ctrl.abort());\n\n // Run validators read-only — does NOT write to fieldErrors or fire requestNotify.\n // Each result is yielded as it settles; the form-level validator is yielded last.\n Promise.all(\n fields.map(async (f) => {\n if (combined.aborted) return;\n\n try {\n const error = await runFieldValidator(f, combined);\n\n if (!combined.aborted) queue.push({ error, field: f });\n } catch (err) {\n if (!isAbortError(err)) throw err;\n\n // aborted or disposed — skip this field\n }\n }),\n )\n .then(async () => {\n // Yield the form-level validator result last (F4: streams _form key too)\n if (ctx.formValidator && !combined.aborted) {\n try {\n const formErrors = await runFormValidator(combined);\n\n if (!combined.aborted) {\n const allKeys = new Set([FORM_ERROR, ...Object.keys(formErrors)]);\n\n for (const key of allKeys) {\n queue.push({ error: formErrors[key], field: key });\n }\n }\n } catch {\n // aborted\n }\n }\n })\n .catch((err) => {\n if (!isAbortError(err)) {\n ctrl.abort();\n queue.fail(err);\n }\n })\n .finally(() => {\n ctrl.abort();\n queue.finish();\n });\n\n return queue.iterator;\n }\n\n return { runValidationCore, submit, submitOrThrow, validate, validateFields, validateStream };\n}\n\nexport type ValidationOps<TValues extends Record<string, unknown>> = ReturnType<typeof createValidationOps<TValues>>;\n","import type { ArrayField, FlatKeyOf, TypeAtPath } from '../types';\n\n// Resolves the element type of an array field — mirrored from types.ts.\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : unknown;\n\n/**\n * Builds a cached typed ArrayField helper for a specific field name.\n * The helper closes over `store` and `set` from the parent form closure.\n * R4: Return type is now `ArrayField<ElementOf<V>>` so callers get type-safe append/insert/etc.\n */\nexport function createArrayField<TValues extends Record<string, unknown>, K extends FlatKeyOf<TValues>>(\n name: K,\n store: Map<string, unknown>,\n set: (name: string, value: unknown) => void,\n): ArrayField<ElementOf<TypeAtPath<TValues, K>>> {\n type T = ElementOf<TypeAtPath<TValues, K>>;\n\n const key = name as string;\n\n return {\n append(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) return;\n\n set(name, (Array.isArray(current) ? [...current, value] : [value]) as TypeAtPath<TValues, K>);\n },\n insert(index: number, value: T) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next.splice(index, 0, value);\n set(name, next as TypeAtPath<TValues, K>);\n },\n move(from: number, to: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next.splice(to, 0, next.splice(from, 1)[0]);\n set(name, next as TypeAtPath<TValues, K>);\n },\n prepend(value: T) {\n const current = store.get(key);\n\n if (current !== undefined && !Array.isArray(current)) return;\n\n set(name, (Array.isArray(current) ? [value, ...current] : [value]) as TypeAtPath<TValues, K>);\n },\n remove(index: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n set(name, current.filter((_, i) => i !== index) as TypeAtPath<TValues, K>);\n },\n replace(index: number, value: T) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n next[index] = value;\n set(name, next as TypeAtPath<TValues, K>);\n },\n swap(a: number, b: number) {\n const current = store.get(key);\n\n if (!Array.isArray(current)) return;\n\n const next = [...current];\n\n [next[a], next[b]] = [next[b], next[a]];\n set(name, next as TypeAtPath<TValues, K>);\n },\n };\n}\n","import type { FormContext } from './context';\n\nimport { warn } from '../_dev';\nimport { assertSafeKey, flattenValues, sanitizeForLog, unflattenValues } from '../_utils';\nimport {\n type ArrayField,\n type ErrorKeyOf,\n type FieldState,\n type FieldValidator,\n type FlatKeyOf,\n type RegisterFieldOptions,\n type SetOptions,\n type TypeAtPath,\n type Unsubscribe,\n} from '../types';\nimport { createArrayField } from './array';\n\n/**\n * Value/field-mutation operations: get/set/values, dirty tracking, errors, dynamic field\n * registration, touch, and reset/replace/patch/array. Moved verbatim from `createForm()`'s\n * \"Value access\" through \"Lifecycle: field operations\" sections.\n */\nexport function createValueOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>) {\n function get<K extends FlatKeyOf<TValues>>(name: K): TypeAtPath<TValues, K> {\n const key = name as string;\n\n assertSafeKey(key);\n\n return ctx.store.get(key) as TypeAtPath<TValues, K>;\n }\n\n function values(): TValues {\n return (ctx.cachedValues ??= unflattenValues(Object.fromEntries(ctx.store)) as TValues);\n }\n\n /* ======== Dirty tracking ======== */\n\n function trackDirty(name: string, value: unknown): void {\n if (ctx.baseline.get(name) === value) ctx.dirty.delete(name);\n else ctx.dirty.add(name);\n }\n\n /* ======== Field mutation ======== */\n\n function set<K extends FlatKeyOf<TValues>>(name: K, value: TypeAtPath<TValues, K>, options: SetOptions = {}): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.store.set(key, value);\n trackDirty(key, value);\n\n if (options.touched) ctx.touched.add(key);\n\n ctx.requestNotify(key);\n }\n\n function field<K extends FlatKeyOf<TValues>>(name: K): FieldState<TypeAtPath<TValues, K>> {\n const key = name as string;\n\n assertSafeKey(key);\n\n return ctx.getFieldSnapshot(key) as FieldState<TypeAtPath<TValues, K>>;\n }\n\n function setError(name: ErrorKeyOf<TValues>, message: string): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.fieldErrors.set(key, message);\n ctx.invalidateErrors();\n ctx.requestNotify(key);\n }\n\n function clearError(name: ErrorKeyOf<TValues>): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n\n if (ctx.fieldErrors.delete(key)) {\n ctx.invalidateErrors();\n ctx.requestNotify(key);\n }\n }\n\n function resetErrors(nextErrors: Partial<Record<ErrorKeyOf<TValues>, string | undefined>> = {}): void {\n ctx.ensureNotDisposed();\n\n ctx.fieldErrors.clear();\n\n for (const [key, message] of Object.entries(nextErrors)) {\n assertSafeKey(key);\n\n if (typeof message === 'string') ctx.fieldErrors.set(key, message);\n }\n\n ctx.invalidateErrors();\n ctx.requestNotify();\n }\n\n /**\n * Warns when `key` looks like a dot-notation array item path (e.g. \"tags.0\") that isn't\n * already a real store entry — array items are stored as whole arrays, not individual keys.\n * Shared by `setValidator()` and `registerField()`.\n */\n function warnIfArrayItemKey(key: string, apiLabel: string): void {\n if (/\\.\\d+(\\.|$)/.test(key) && !ctx.store.has(key)) {\n const displayKey = sanitizeForLog(key, 80);\n\n warn(\n `${apiLabel}('${displayKey}'): path looks like an array item key. ` +\n `Array items are stored as whole arrays — register on the parent key instead.`,\n );\n }\n }\n\n function setValidator(name: FlatKeyOf<TValues>, validator?: FieldValidator<unknown>): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n warnIfArrayItemKey(key, 'setValidator');\n\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n\n if (validator) {\n ctx.validators.set(key, validator);\n } else {\n ctx.validators.delete(key);\n\n if (ctx.fieldErrors.delete(key)) {\n ctx.invalidateErrors();\n ctx.requestNotify(key);\n }\n }\n }\n\n /* ======== Dynamic field registration ======== */\n\n function registerField<K extends FlatKeyOf<TValues>>(\n name: K,\n options: RegisterFieldOptions<TypeAtPath<TValues, K>> = {},\n ): Unsubscribe {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n warnIfArrayItemKey(key, 'fields.register');\n\n if (options.validator !== undefined) setValidator(name, options.validator as FieldValidator<unknown>);\n\n if (options.defaultValue !== undefined && !ctx.store.has(key)) {\n ctx.store.set(key, options.defaultValue);\n ctx.baseline.set(key, options.defaultValue);\n ctx.requestNotify(key);\n }\n\n return () => {\n if (!ctx.disposed) removeField(name);\n };\n }\n\n /* ======== Field enumeration ======== */\n\n function listFields(): readonly string[] {\n const known = new Set<string>();\n\n for (const name of ctx.store.keys()) known.add(name);\n for (const name of ctx.validators.keys()) known.add(name);\n\n return Object.freeze([...known]);\n }\n\n /* ======== Touch ======== */\n\n function touch(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.touched.add(key);\n ctx.requestNotify(key);\n }\n\n function untouch(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n\n if (ctx.touched.delete(key)) ctx.requestNotify(key);\n }\n\n function touchAll(): void {\n ctx.ensureNotDisposed();\n\n for (const name of ctx.store.keys()) ctx.touched.add(name);\n for (const name of ctx.validators.keys()) ctx.touched.add(name);\n\n ctx.requestNotify();\n }\n\n function untouchAll(): void {\n ctx.ensureNotDisposed();\n ctx.touched.clear();\n ctx.requestNotify();\n }\n\n /* ======== Lifecycle: field operations ======== */\n\n function resetField(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n ctx.store.set(key, ctx.baseline.get(key));\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n\n ctx.requestNotify(key);\n }\n\n function removeField(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n ctx.store.delete(key);\n ctx.baseline.delete(key);\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n ctx.validators.delete(key);\n ctx.arrayCache.delete(key);\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n\n ctx.requestNotify(key);\n }\n\n function reset(): void {\n ctx.ensureNotDisposed();\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.fieldCtrls.clear();\n ctx.validatingRuns.clear();\n ctx.store.clear();\n ctx.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n ctx.submitCount = 0;\n ctx.invalidateErrors();\n\n for (const [name, value] of ctx.baseline) ctx.store.set(name, value);\n\n ctx.requestNotify();\n }\n\n function replace(newValues: TValues): void {\n ctx.ensureNotDisposed();\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n for (const ctrl of ctx.fieldCtrls.values()) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.fieldCtrls.clear();\n ctx.validatingRuns.clear();\n\n const flat = flattenValues(newValues as Record<string, unknown>);\n\n ctx.store.clear();\n ctx.baseline.clear();\n ctx.fieldErrors.clear();\n ctx.touched.clear();\n ctx.dirty.clear();\n ctx.submitCount = 0;\n ctx.invalidateErrors();\n\n for (const [name, value] of Object.entries(flat)) {\n ctx.store.set(name, value);\n ctx.baseline.set(name, value);\n }\n\n ctx.requestNotify();\n }\n\n function patch(partial: Record<string, unknown>): void {\n ctx.ensureNotDisposed();\n\n const flat = flattenValues(partial);\n\n for (const [key, value] of Object.entries(flat)) {\n ctx.baseline.set(key, value);\n ctx.store.set(key, value);\n ctx.dirty.delete(key);\n }\n\n ctx.requestNotify(Object.keys(flat));\n }\n\n /* ======== Array helpers ======== */\n\n function array(name: FlatKeyOf<TValues>): ArrayField {\n ctx.ensureNotDisposed();\n\n const key = name as string;\n\n assertSafeKey(key);\n\n const cached = ctx.arrayCache.get(key);\n\n if (cached) return cached;\n\n const helpers = createArrayField(name, ctx.store, set as (name: string, value: unknown) => void);\n\n ctx.arrayCache.set(key, helpers);\n\n return helpers;\n }\n\n return {\n array,\n clearError,\n field,\n get,\n listFields,\n patch,\n registerField,\n removeField,\n replace,\n reset,\n resetErrors,\n resetField,\n set,\n setError,\n setValidator,\n touch,\n touchAll,\n untouch,\n untouchAll,\n values,\n };\n}\n\nexport type ValueOps<TValues extends Record<string, unknown>> = ReturnType<typeof createValueOps<TValues>>;\n","import { warn } from './_dev';\nimport { sanitizeForLog } from './_utils';\nimport { createFormContext } from './core/context';\nimport { createLifecycleOps } from './core/lifecycle';\nimport { createObserveOps } from './core/observe';\nimport { createScopedForm, type ScopeContext } from './core/scope';\nimport { createValidationOps } from './core/validation';\nimport { createValueOps } from './core/values';\nimport { type FlatKeyOf, type Form, type FormOptions, type ScopedValues } from './types';\n\n/* -------------------- createForm -------------------- */\n\nexport function createForm<TInit extends object>(\n init: Omit<FormOptions<TInit & Record<string, unknown>>, 'defaultValues'> & {\n defaultValues: TInit | (() => Promise<TInit>);\n },\n): Form<TInit & Record<string, unknown>>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init?: FormOptions<TValues>,\n): Form<TValues>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init: FormOptions<TValues> = {},\n): Form<TValues> {\n const ctx = createFormContext<TValues>(init);\n const valueOps = createValueOps(ctx);\n const validationOps = createValidationOps(ctx, { touchAll: valueOps.touchAll, values: valueOps.values });\n const observeOps = createObserveOps(ctx, {\n set: valueOps.set,\n touch: valueOps.touch,\n validateFields: validationOps.validateFields,\n });\n const lifecycleOps = createLifecycleOps(ctx);\n\n /* ---- Async defaultValues ---- */\n\n if (typeof init.defaultValues === 'function') {\n void init\n .defaultValues()\n .then((resolved) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n valueOps.replace(resolved);\n })\n .catch((err: unknown) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n warn(`defaultValues factory rejected. Form will be empty. Error: ${sanitizeForLog(String(err))}`);\n init.onLoadError?.(err);\n ctx.requestNotify();\n });\n }\n\n /* ======== Public form object ======== */\n\n // registerField/removeField/setValidator/listFields are nested under `fields`, not top-level.\n const { listFields, registerField, removeField, setValidator, ...publicValueOps } = valueOps;\n // runValidationCore is internal-only — needed by scope(), not part of the public Form<T> surface.\n const { runValidationCore, ...publicValidationOps } = validationOps;\n\n // `scope` is defined as a method on publicForm so that `const publicForm` can capture itself\n // via closure without a forward-reference lint disable.\n const publicForm: Form<TValues> = {\n ...publicValueOps,\n ...publicValidationOps,\n ...observeOps,\n ...lifecycleOps,\n batch: ctx.batch,\n get disposalSignal() {\n return ctx.disposeController.signal;\n },\n get disposed() {\n return ctx.disposed;\n },\n fields: {\n list: listFields,\n register: registerField,\n remove: removeField,\n setValidator,\n },\n get isLoading() {\n return ctx.loadingState;\n },\n get isSubmitting() {\n return ctx.isSubmittingState;\n },\n scope<P extends FlatKeyOf<TValues>>(prefix: P): Form<ScopedValues<TValues, P>> {\n const key = prefix as string;\n const cached = ctx.scopeCache.get(key);\n\n if (cached) return cached as Form<ScopedValues<TValues, P>>;\n\n const scopeCtx: ScopeContext<TValues> = {\n baseline: ctx.baseline,\n dirty: ctx.dirty,\n ensureNotDisposed: ctx.ensureNotDisposed,\n fieldCtrls: ctx.fieldCtrls,\n fieldErrors: ctx.fieldErrors,\n getStateSnapshot: ctx.getStateSnapshot,\n incrementSubmitCount: () => {\n ctx.submitCount++;\n },\n invalidateErrors: ctx.invalidateErrors,\n isDisposed: () => ctx.disposed,\n isSubmitting: () => ctx.isSubmittingState,\n requestNotify: ctx.requestNotify,\n root: publicForm,\n runValidationCore,\n setSubmitting: (v) => {\n ctx.isSubmittingState = v;\n },\n store: ctx.store,\n touched: ctx.touched,\n validators: ctx.validators,\n };\n\n const scoped = createScopedForm<TValues, P>(scopeCtx, prefix) as Form<ScopedValues<TValues, P>>;\n\n ctx.scopeCache.set(key, scoped as Form<never>);\n\n return scoped;\n },\n get state() {\n return ctx.getStateSnapshot();\n },\n // On a root form, subscribeScoped behaves identically to subscribe — no prefix filtering.\n subscribeScoped: observeOps.subscribe,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n\n return publicForm;\n}\n","import { flattenValues } from '../_utils';\n\n/**\n * Converts a form values object into a `FormData` instance.\n * Nested objects are flattened to dot-notation keys (e.g. `user.name`).\n * `File`, `Blob`, and `FileList` values are appended as-is; all others are coerced to strings.\n * `null` and `undefined` values are omitted.\n */\nexport function toFormData(values: Record<string, unknown>): FormData {\n const fd = new FormData();\n\n for (const [name, value] of Object.entries(flattenValues(values))) {\n if (value === null || value === undefined) continue;\n\n if (value instanceof File || value instanceof Blob) {\n fd.append(name, value);\n } else if (value instanceof FileList) {\n for (let i = 0; i < value.length; i++) fd.append(name, value[i]);\n } else if (Array.isArray(value)) {\n for (const item of value) {\n fd.append(name, item instanceof File || item instanceof Blob ? item : String(item));\n }\n } else {\n fd.append(name, String(value));\n }\n }\n\n return fd;\n}\n"],"mappings":"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"}
|
package/dist/form.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./_utils.cjs"),t=require("./_dev.cjs"),n=require("./core/context.cjs"),r=require("./core/lifecycle.cjs"),i=require("./core/observe.cjs"),a=require("./core/scope.cjs"),o=require("./core/validation.cjs"),s=require("./core/values.cjs");function c(c={}){let l=n.createFormContext(c),u=s.createValueOps(l),d=o.createValidationOps(l,{touchAll:u.touchAll,values:u.values}),f=i.createObserveOps(l,{set:u.set,touch:u.touch,validateFields:d.validateFields}),p=r.createLifecycleOps(l);typeof c.defaultValues==`function`&&c.defaultValues().then(e=>{l.loadingState=!1,!l.disposed&&u.replace(e)}).catch(n=>{l.loadingState=!1,!l.disposed&&(t.warn(`defaultValues factory rejected. Form will be empty. Error: ${e.sanitizeForLog(String(n))}`),c.onLoadError?.(n),l.requestNotify())});let{listFields:m,registerField:h,removeField:g,setValidator:_,...v}=u,{runValidationCore:y,...b}=d,x={...v,...b,...f,...p,batch:l.batch,get disposalSignal(){return l.disposeController.signal},get disposed(){return l.disposed},fields:{list:m,register:h,remove:g,setValidator:_},get isLoading(){return l.loadingState},get isSubmitting(){return l.isSubmittingState},scope(e){let t=e,n=l.scopeCache.get(t);if(n)return n;let r=a.createScopedForm({baseline:l.baseline,dirty:l.dirty,ensureNotDisposed:l.ensureNotDisposed,fieldCtrls:l.fieldCtrls,fieldErrors:l.fieldErrors,getStateSnapshot:l.getStateSnapshot,incrementSubmitCount:()=>{l.submitCount++},invalidateErrors:l.invalidateErrors,isDisposed:()=>l.disposed,isSubmitting:()=>l.isSubmittingState,requestNotify:l.requestNotify,root:x,runValidationCore:y,setSubmitting:e=>{l.isSubmittingState=e},store:l.store,touched:l.touched,validators:l.validators},e);return l.scopeCache.set(t,r),r},get state(){return l.getStateSnapshot()},subscribeScoped:f.subscribe,[Symbol.dispose](){this.dispose()}};return x}exports.createForm=c;
|
|
2
|
+
//# sourceMappingURL=form.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.cjs","names":[],"sources":["../src/form.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { sanitizeForLog } from './_utils';\nimport { createFormContext } from './core/context';\nimport { createLifecycleOps } from './core/lifecycle';\nimport { createObserveOps } from './core/observe';\nimport { createScopedForm, type ScopeContext } from './core/scope';\nimport { createValidationOps } from './core/validation';\nimport { createValueOps } from './core/values';\nimport { type FlatKeyOf, type Form, type FormOptions, type ScopedValues } from './types';\n\n/* -------------------- createForm -------------------- */\n\nexport function createForm<TInit extends object>(\n init: Omit<FormOptions<TInit & Record<string, unknown>>, 'defaultValues'> & {\n defaultValues: TInit | (() => Promise<TInit>);\n },\n): Form<TInit & Record<string, unknown>>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init?: FormOptions<TValues>,\n): Form<TValues>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init: FormOptions<TValues> = {},\n): Form<TValues> {\n const ctx = createFormContext<TValues>(init);\n const valueOps = createValueOps(ctx);\n const validationOps = createValidationOps(ctx, { touchAll: valueOps.touchAll, values: valueOps.values });\n const observeOps = createObserveOps(ctx, {\n set: valueOps.set,\n touch: valueOps.touch,\n validateFields: validationOps.validateFields,\n });\n const lifecycleOps = createLifecycleOps(ctx);\n\n /* ---- Async defaultValues ---- */\n\n if (typeof init.defaultValues === 'function') {\n void init\n .defaultValues()\n .then((resolved) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n valueOps.replace(resolved);\n })\n .catch((err: unknown) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n warn(`defaultValues factory rejected. Form will be empty. Error: ${sanitizeForLog(String(err))}`);\n init.onLoadError?.(err);\n ctx.requestNotify();\n });\n }\n\n /* ======== Public form object ======== */\n\n // registerField/removeField/setValidator/listFields are nested under `fields`, not top-level.\n const { listFields, registerField, removeField, setValidator, ...publicValueOps } = valueOps;\n // runValidationCore is internal-only — needed by scope(), not part of the public Form<T> surface.\n const { runValidationCore, ...publicValidationOps } = validationOps;\n\n // `scope` is defined as a method on publicForm so that `const publicForm` can capture itself\n // via closure without a forward-reference lint disable.\n const publicForm: Form<TValues> = {\n ...publicValueOps,\n ...publicValidationOps,\n ...observeOps,\n ...lifecycleOps,\n batch: ctx.batch,\n get disposalSignal() {\n return ctx.disposeController.signal;\n },\n get disposed() {\n return ctx.disposed;\n },\n fields: {\n list: listFields,\n register: registerField,\n remove: removeField,\n setValidator,\n },\n get isLoading() {\n return ctx.loadingState;\n },\n get isSubmitting() {\n return ctx.isSubmittingState;\n },\n scope<P extends FlatKeyOf<TValues>>(prefix: P): Form<ScopedValues<TValues, P>> {\n const key = prefix as string;\n const cached = ctx.scopeCache.get(key);\n\n if (cached) return cached as Form<ScopedValues<TValues, P>>;\n\n const scopeCtx: ScopeContext<TValues> = {\n baseline: ctx.baseline,\n dirty: ctx.dirty,\n ensureNotDisposed: ctx.ensureNotDisposed,\n fieldCtrls: ctx.fieldCtrls,\n fieldErrors: ctx.fieldErrors,\n getStateSnapshot: ctx.getStateSnapshot,\n incrementSubmitCount: () => {\n ctx.submitCount++;\n },\n invalidateErrors: ctx.invalidateErrors,\n isDisposed: () => ctx.disposed,\n isSubmitting: () => ctx.isSubmittingState,\n requestNotify: ctx.requestNotify,\n root: publicForm,\n runValidationCore,\n setSubmitting: (v) => {\n ctx.isSubmittingState = v;\n },\n store: ctx.store,\n touched: ctx.touched,\n validators: ctx.validators,\n };\n\n const scoped = createScopedForm<TValues, P>(scopeCtx, prefix) as Form<ScopedValues<TValues, P>>;\n\n ctx.scopeCache.set(key, scoped as Form<never>);\n\n return scoped;\n },\n get state() {\n return ctx.getStateSnapshot();\n },\n // On a root form, subscribeScoped behaves identically to subscribe — no prefix filtering.\n subscribeScoped: observeOps.subscribe,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n\n return publicForm;\n}\n"],"mappings":"0PAoBA,SAAgB,EACd,EAA6B,CAAC,EACf,CACf,IAAM,EAAM,EAAA,kBAA2B,CAAI,EACrC,EAAW,EAAA,eAAe,CAAG,EAC7B,EAAgB,EAAA,oBAAoB,EAAK,CAAE,SAAU,EAAS,SAAU,OAAQ,EAAS,MAAO,CAAC,EACjG,EAAa,EAAA,iBAAiB,EAAK,CACvC,IAAK,EAAS,IACd,MAAO,EAAS,MAChB,eAAgB,EAAc,cAChC,CAAC,EACK,EAAe,EAAA,mBAAmB,CAAG,EAIvC,OAAO,EAAK,eAAkB,YAChC,EACG,cAAc,CAAC,CACf,KAAM,GAAa,CAClB,EAAI,aAAe,GAEf,GAAI,UAER,EAAS,QAAQ,CAAQ,CAC3B,CAAC,CAAC,CACD,MAAO,GAAiB,CACvB,EAAI,aAAe,GAEf,GAAI,WAER,EAAA,KAAK,8DAA8D,EAAA,eAAe,OAAO,CAAG,CAAC,GAAG,EAChG,EAAK,cAAc,CAAG,EACtB,EAAI,cAAc,EACpB,CAAC,EAML,GAAM,CAAE,aAAY,gBAAe,cAAa,eAAc,GAAG,GAAmB,EAE9E,CAAE,oBAAmB,GAAG,GAAwB,EAIhD,EAA4B,CAChC,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,MAAO,EAAI,MACX,IAAI,gBAAiB,CACnB,OAAO,EAAI,kBAAkB,MAC/B,EACA,IAAI,UAAW,CACb,OAAO,EAAI,QACb,EACA,OAAQ,CACN,KAAM,EACN,SAAU,EACV,OAAQ,EACR,cACF,EACA,IAAI,WAAY,CACd,OAAO,EAAI,YACb,EACA,IAAI,cAAe,CACjB,OAAO,EAAI,iBACb,EACA,MAAoC,EAA2C,CAC7E,IAAM,EAAM,EACN,EAAS,EAAI,WAAW,IAAI,CAAG,EAErC,GAAI,EAAQ,OAAO,EA0BnB,IAAM,EAAS,EAAA,iBAA6B,CAvB1C,SAAU,EAAI,SACd,MAAO,EAAI,MACX,kBAAmB,EAAI,kBACvB,WAAY,EAAI,WAChB,YAAa,EAAI,YACjB,iBAAkB,EAAI,iBACtB,yBAA4B,CAC1B,EAAI,aACN,EACA,iBAAkB,EAAI,iBACtB,eAAkB,EAAI,SACtB,iBAAoB,EAAI,kBACxB,cAAe,EAAI,cACnB,KAAM,EACN,oBACA,cAAgB,GAAM,CACpB,EAAI,kBAAoB,CAC1B,EACA,MAAO,EAAI,MACX,QAAS,EAAI,QACb,WAAY,EAAI,UAG0B,EAAU,CAAM,EAI5D,OAFA,EAAI,WAAW,IAAI,EAAK,CAAqB,EAEtC,CACT,EACA,IAAI,OAAQ,CACV,OAAO,EAAI,iBAAiB,CAC9B,EAEA,gBAAiB,EAAW,UAC5B,CAAC,OAAO,UAAW,CACjB,KAAK,QAAQ,CACf,CACF,EAEA,OAAO,CACT"}
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Form, type FormOptions } from './types';
|
|
2
|
+
export declare function createForm<TInit extends object>(init: Omit<FormOptions<TInit & Record<string, unknown>>, 'defaultValues'> & {
|
|
3
|
+
defaultValues: TInit | (() => Promise<TInit>);
|
|
4
|
+
}): Form<TInit & Record<string, unknown>>;
|
|
5
|
+
export declare function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(init?: FormOptions<TValues>): Form<TValues>;
|
|
6
|
+
//# sourceMappingURL=form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"AAQA,OAAO,EAAkB,KAAK,IAAI,EAAE,KAAK,WAAW,EAAqB,MAAM,SAAS,CAAC;AAIzF,wBAAgB,UAAU,CAAC,KAAK,SAAS,MAAM,EAC7C,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG;IAC1E,aAAa,EAAE,KAAK,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;CAC/C,GACA,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AACzC,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1F,IAAI,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,GAC1B,IAAI,CAAC,OAAO,CAAC,CAAC"}
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { sanitizeForLog as e } from "./_utils.js";
|
|
2
|
+
import { warn as t } from "./_dev.js";
|
|
3
|
+
import { createFormContext as n } from "./core/context.js";
|
|
4
|
+
import { createLifecycleOps as r } from "./core/lifecycle.js";
|
|
5
|
+
import { createObserveOps as i } from "./core/observe.js";
|
|
6
|
+
import { createScopedForm as a } from "./core/scope.js";
|
|
7
|
+
import { createValidationOps as o } from "./core/validation.js";
|
|
8
|
+
import { createValueOps as s } from "./core/values.js";
|
|
9
|
+
//#region src/form.ts
|
|
10
|
+
function c(c = {}) {
|
|
11
|
+
let l = n(c), u = s(l), d = o(l, {
|
|
12
|
+
touchAll: u.touchAll,
|
|
13
|
+
values: u.values
|
|
14
|
+
}), f = i(l, {
|
|
15
|
+
set: u.set,
|
|
16
|
+
touch: u.touch,
|
|
17
|
+
validateFields: d.validateFields
|
|
18
|
+
}), p = r(l);
|
|
19
|
+
typeof c.defaultValues == "function" && c.defaultValues().then((e) => {
|
|
20
|
+
l.loadingState = !1, !l.disposed && u.replace(e);
|
|
21
|
+
}).catch((n) => {
|
|
22
|
+
l.loadingState = !1, !l.disposed && (t(`defaultValues factory rejected. Form will be empty. Error: ${e(String(n))}`), c.onLoadError?.(n), l.requestNotify());
|
|
23
|
+
});
|
|
24
|
+
let { listFields: m, registerField: h, removeField: g, setValidator: _, ...v } = u, { runValidationCore: y, ...b } = d, x = {
|
|
25
|
+
...v,
|
|
26
|
+
...b,
|
|
27
|
+
...f,
|
|
28
|
+
...p,
|
|
29
|
+
batch: l.batch,
|
|
30
|
+
get disposalSignal() {
|
|
31
|
+
return l.disposeController.signal;
|
|
32
|
+
},
|
|
33
|
+
get disposed() {
|
|
34
|
+
return l.disposed;
|
|
35
|
+
},
|
|
36
|
+
fields: {
|
|
37
|
+
list: m,
|
|
38
|
+
register: h,
|
|
39
|
+
remove: g,
|
|
40
|
+
setValidator: _
|
|
41
|
+
},
|
|
42
|
+
get isLoading() {
|
|
43
|
+
return l.loadingState;
|
|
44
|
+
},
|
|
45
|
+
get isSubmitting() {
|
|
46
|
+
return l.isSubmittingState;
|
|
47
|
+
},
|
|
48
|
+
scope(e) {
|
|
49
|
+
let t = e, n = l.scopeCache.get(t);
|
|
50
|
+
if (n) return n;
|
|
51
|
+
let r = a({
|
|
52
|
+
baseline: l.baseline,
|
|
53
|
+
dirty: l.dirty,
|
|
54
|
+
ensureNotDisposed: l.ensureNotDisposed,
|
|
55
|
+
fieldCtrls: l.fieldCtrls,
|
|
56
|
+
fieldErrors: l.fieldErrors,
|
|
57
|
+
getStateSnapshot: l.getStateSnapshot,
|
|
58
|
+
incrementSubmitCount: () => {
|
|
59
|
+
l.submitCount++;
|
|
60
|
+
},
|
|
61
|
+
invalidateErrors: l.invalidateErrors,
|
|
62
|
+
isDisposed: () => l.disposed,
|
|
63
|
+
isSubmitting: () => l.isSubmittingState,
|
|
64
|
+
requestNotify: l.requestNotify,
|
|
65
|
+
root: x,
|
|
66
|
+
runValidationCore: y,
|
|
67
|
+
setSubmitting: (e) => {
|
|
68
|
+
l.isSubmittingState = e;
|
|
69
|
+
},
|
|
70
|
+
store: l.store,
|
|
71
|
+
touched: l.touched,
|
|
72
|
+
validators: l.validators
|
|
73
|
+
}, e);
|
|
74
|
+
return l.scopeCache.set(t, r), r;
|
|
75
|
+
},
|
|
76
|
+
get state() {
|
|
77
|
+
return l.getStateSnapshot();
|
|
78
|
+
},
|
|
79
|
+
subscribeScoped: f.subscribe,
|
|
80
|
+
[Symbol.dispose]() {
|
|
81
|
+
this.dispose();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return x;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { c as createForm };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=form.js.map
|
package/dist/form.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.js","names":[],"sources":["../src/form.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { sanitizeForLog } from './_utils';\nimport { createFormContext } from './core/context';\nimport { createLifecycleOps } from './core/lifecycle';\nimport { createObserveOps } from './core/observe';\nimport { createScopedForm, type ScopeContext } from './core/scope';\nimport { createValidationOps } from './core/validation';\nimport { createValueOps } from './core/values';\nimport { type FlatKeyOf, type Form, type FormOptions, type ScopedValues } from './types';\n\n/* -------------------- createForm -------------------- */\n\nexport function createForm<TInit extends object>(\n init: Omit<FormOptions<TInit & Record<string, unknown>>, 'defaultValues'> & {\n defaultValues: TInit | (() => Promise<TInit>);\n },\n): Form<TInit & Record<string, unknown>>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init?: FormOptions<TValues>,\n): Form<TValues>;\nexport function createForm<TValues extends Record<string, unknown> = Record<string, unknown>>(\n init: FormOptions<TValues> = {},\n): Form<TValues> {\n const ctx = createFormContext<TValues>(init);\n const valueOps = createValueOps(ctx);\n const validationOps = createValidationOps(ctx, { touchAll: valueOps.touchAll, values: valueOps.values });\n const observeOps = createObserveOps(ctx, {\n set: valueOps.set,\n touch: valueOps.touch,\n validateFields: validationOps.validateFields,\n });\n const lifecycleOps = createLifecycleOps(ctx);\n\n /* ---- Async defaultValues ---- */\n\n if (typeof init.defaultValues === 'function') {\n void init\n .defaultValues()\n .then((resolved) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n valueOps.replace(resolved);\n })\n .catch((err: unknown) => {\n ctx.loadingState = false;\n\n if (ctx.disposed) return;\n\n warn(`defaultValues factory rejected. Form will be empty. Error: ${sanitizeForLog(String(err))}`);\n init.onLoadError?.(err);\n ctx.requestNotify();\n });\n }\n\n /* ======== Public form object ======== */\n\n // registerField/removeField/setValidator/listFields are nested under `fields`, not top-level.\n const { listFields, registerField, removeField, setValidator, ...publicValueOps } = valueOps;\n // runValidationCore is internal-only — needed by scope(), not part of the public Form<T> surface.\n const { runValidationCore, ...publicValidationOps } = validationOps;\n\n // `scope` is defined as a method on publicForm so that `const publicForm` can capture itself\n // via closure without a forward-reference lint disable.\n const publicForm: Form<TValues> = {\n ...publicValueOps,\n ...publicValidationOps,\n ...observeOps,\n ...lifecycleOps,\n batch: ctx.batch,\n get disposalSignal() {\n return ctx.disposeController.signal;\n },\n get disposed() {\n return ctx.disposed;\n },\n fields: {\n list: listFields,\n register: registerField,\n remove: removeField,\n setValidator,\n },\n get isLoading() {\n return ctx.loadingState;\n },\n get isSubmitting() {\n return ctx.isSubmittingState;\n },\n scope<P extends FlatKeyOf<TValues>>(prefix: P): Form<ScopedValues<TValues, P>> {\n const key = prefix as string;\n const cached = ctx.scopeCache.get(key);\n\n if (cached) return cached as Form<ScopedValues<TValues, P>>;\n\n const scopeCtx: ScopeContext<TValues> = {\n baseline: ctx.baseline,\n dirty: ctx.dirty,\n ensureNotDisposed: ctx.ensureNotDisposed,\n fieldCtrls: ctx.fieldCtrls,\n fieldErrors: ctx.fieldErrors,\n getStateSnapshot: ctx.getStateSnapshot,\n incrementSubmitCount: () => {\n ctx.submitCount++;\n },\n invalidateErrors: ctx.invalidateErrors,\n isDisposed: () => ctx.disposed,\n isSubmitting: () => ctx.isSubmittingState,\n requestNotify: ctx.requestNotify,\n root: publicForm,\n runValidationCore,\n setSubmitting: (v) => {\n ctx.isSubmittingState = v;\n },\n store: ctx.store,\n touched: ctx.touched,\n validators: ctx.validators,\n };\n\n const scoped = createScopedForm<TValues, P>(scopeCtx, prefix) as Form<ScopedValues<TValues, P>>;\n\n ctx.scopeCache.set(key, scoped as Form<never>);\n\n return scoped;\n },\n get state() {\n return ctx.getStateSnapshot();\n },\n // On a root form, subscribeScoped behaves identically to subscribe — no prefix filtering.\n subscribeScoped: observeOps.subscribe,\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n\n return publicForm;\n}\n"],"mappings":";;;;;;;;;AAoBA,SAAgB,EACd,IAA6B,CAAC,GACf;CACf,IAAM,IAAM,EAA2B,CAAI,GACrC,IAAW,EAAe,CAAG,GAC7B,IAAgB,EAAoB,GAAK;EAAE,UAAU,EAAS;EAAU,QAAQ,EAAS;CAAO,CAAC,GACjG,IAAa,EAAiB,GAAK;EACvC,KAAK,EAAS;EACd,OAAO,EAAS;EAChB,gBAAgB,EAAc;CAChC,CAAC,GACK,IAAe,EAAmB,CAAG;CAI3C,AAAI,OAAO,EAAK,iBAAkB,cAChC,EACG,cAAc,CAAC,CACf,MAAM,MAAa;EAClB,EAAI,eAAe,IAEf,GAAI,YAER,EAAS,QAAQ,CAAQ;CAC3B,CAAC,CAAC,CACD,OAAO,MAAiB;EACvB,EAAI,eAAe,IAEf,GAAI,aAER,EAAK,8DAA8D,EAAe,OAAO,CAAG,CAAC,GAAG,GAChG,EAAK,cAAc,CAAG,GACtB,EAAI,cAAc;CACpB,CAAC;CAML,IAAM,EAAE,eAAY,kBAAe,gBAAa,iBAAc,GAAG,MAAmB,GAE9E,EAAE,sBAAmB,GAAG,MAAwB,GAIhD,IAA4B;EAChC,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,OAAO,EAAI;EACX,IAAI,iBAAiB;GACnB,OAAO,EAAI,kBAAkB;EAC/B;EACA,IAAI,WAAW;GACb,OAAO,EAAI;EACb;EACA,QAAQ;GACN,MAAM;GACN,UAAU;GACV,QAAQ;GACR;EACF;EACA,IAAI,YAAY;GACd,OAAO,EAAI;EACb;EACA,IAAI,eAAe;GACjB,OAAO,EAAI;EACb;EACA,MAAoC,GAA2C;GAC7E,IAAM,IAAM,GACN,IAAS,EAAI,WAAW,IAAI,CAAG;GAErC,IAAI,GAAQ,OAAO;GA0BnB,IAAM,IAAS,EAA6B;IAvB1C,UAAU,EAAI;IACd,OAAO,EAAI;IACX,mBAAmB,EAAI;IACvB,YAAY,EAAI;IAChB,aAAa,EAAI;IACjB,kBAAkB,EAAI;IACtB,4BAA4B;KAC1B,EAAI;IACN;IACA,kBAAkB,EAAI;IACtB,kBAAkB,EAAI;IACtB,oBAAoB,EAAI;IACxB,eAAe,EAAI;IACnB,MAAM;IACN;IACA,gBAAgB,MAAM;KACpB,EAAI,oBAAoB;IAC1B;IACA,OAAO,EAAI;IACX,SAAS,EAAI;IACb,YAAY,EAAI;GAG0B,GAAU,CAAM;GAI5D,OAFA,EAAI,WAAW,IAAI,GAAK,CAAqB,GAEtC;EACT;EACA,IAAI,QAAQ;GACV,OAAO,EAAI,iBAAiB;EAC9B;EAEA,iBAAiB,EAAW;EAC5B,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;CACF;CAEA,OAAO;AACT"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./types.cjs"),n=require("./form.cjs"),r=require("./adapters/form-data.cjs");exports.FORM_ERROR=t.FORM_ERROR,exports.ForgeConfigError=e.ForgeConfigError,exports.ForgeDisposedError=e.ForgeDisposedError,exports.ForgeError=e.ForgeError,exports.ForgeSubmitError=e.ForgeSubmitError,exports.ForgeValidationError=e.ForgeValidationError,exports.ValidationModes=t.ValidationModes,exports.createForm=n.createForm,exports.toFormData=r.toFormData;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ForgeConfigError, ForgeDisposedError, ForgeError, ForgeSubmitError, ForgeValidationError } from './errors';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export { createForm } from './form';
|
|
4
|
+
export { toFormData } from './adapters/form-data';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,UAAU,CAAC;AACpH,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ForgeConfigError as e, ForgeDisposedError as t, ForgeError as n, ForgeSubmitError as r, ForgeValidationError as i } from "./errors.js";
|
|
2
|
+
import { FORM_ERROR as a, ValidationModes as o } from "./types.js";
|
|
3
|
+
import { createForm as s } from "./form.js";
|
|
4
|
+
import { toFormData as c } from "./adapters/form-data.js";
|
|
5
|
+
export { a as FORM_ERROR, e as ForgeConfigError, t as ForgeDisposedError, n as ForgeError, r as ForgeSubmitError, i as ForgeValidationError, o as ValidationModes, s as createForm, c as toFormData };
|
package/dist/types.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["/** A callback to remove a subscription. */\nexport type Unsubscribe = () => void;\n\nexport type MaybePromise<T> = T | PromiseLike<T>;\n\nexport type FieldValidator<V = unknown> = (value: V, signal?: AbortSignal) => MaybePromise<string | undefined>;\n\nexport type FormValidator<TValues extends Record<string, unknown> = Record<string, unknown>> = (\n values: TValues,\n signal?: AbortSignal,\n) => MaybePromise<Partial<Record<ErrorKeyOf<TValues>, string>> | undefined>;\n\nexport type SetOptions = {\n /** Whether to mark the field as touched. Default: false. */\n touched?: boolean;\n};\n\nexport type FormState = {\n errors: Readonly<Record<string, string> & { [FORM_ERROR]?: string }>;\n isDirty: boolean;\n /** `true` while an async `defaultValues` factory is resolving. */\n isLoading: boolean;\n isSubmitting: boolean;\n isTouched: boolean;\n isValid: boolean;\n isValidating: boolean;\n submitCount: number;\n /**\n * Field names that are currently touched, as full dot-notation paths.\n * On a scoped form these are still full paths (e.g. `\"address.city\"`, not `\"city\"`).\n * Prefer calling `scopedForm.validate()` to run scoped validation rather than\n * passing `state.touchedFields` directly — scoped `validate(fields[])` expects relative paths.\n */\n touchedFields: readonly string[];\n /** Field names that currently have an active async validation run. Useful for per-field loading indicators. */\n validatingFields: readonly string[];\n};\n\nexport type SubscribeOptions = {\n /** Call the listener immediately with the current state on subscription. Default: false. */\n sync?: boolean;\n};\n\n/** Shared field key for root-level / form-level validation errors. */\nexport const FORM_ERROR = '_form' as const;\n\nexport type ValidateResult = {\n errors: Readonly<Record<string, string>>;\n valid: boolean;\n};\n\nexport type SubmitResult<TResult = void> =\n { ok: true; value: TResult } | { errors: Record<string, string>; ok: false; type: 'validation' };\n\n/* -------------------- Utility Types -------------------- */\n\n/**\n * Recursively makes all properties optional.\n * Used by `form.patch()` to accept partial nested updates.\n */\nexport type DeepPartial<T> = T extends Record<string, unknown> ? { [K in keyof T]?: DeepPartial<T[K]> } : T;\n\n/**\n * Recursively extracts all dot-notation leaf keys from a values shape.\n * Depth-limited to 5: TypeScript compilation time grows non-linearly beyond this point.\n */\nexport type FlatKeyOf<\n T extends Record<string, unknown>,\n P extends string = '',\n D extends readonly 0[] = [],\n> = D['length'] extends 5\n ? string\n : {\n [K in keyof T & string]: T[K] extends unknown[] | File | Blob | Date\n ? P extends ''\n ? K\n : `${P}.${K}`\n : T[K] extends Record<string, unknown>\n ? FlatKeyOf<T[K] & Record<string, unknown>, P extends '' ? K : `${P}.${K}`, [...D, 0]>\n : P extends ''\n ? K\n : `${P}.${K}`;\n }[keyof T & string];\n\n/** Resolves the value type at a dot-notation path within a values shape. */\nexport type TypeAtPath<T, K extends string> = K extends `${infer Head}.${infer Tail}`\n ? Head extends keyof T\n ? T[Head] extends Record<string, unknown>\n ? TypeAtPath<T[Head], Tail>\n : unknown\n : unknown\n : K extends keyof T\n ? T[K]\n : unknown;\n\n/** Field error keys include field paths plus the reserved `_form` key for root-level issues. */\nexport type ErrorKeyOf<TValues extends Record<string, unknown>> = FlatKeyOf<TValues> | typeof FORM_ERROR;\n\n/**\n * Structural interface for safeParse-compatible schemas.\n * Works with `@vielzeug/spell`, Zod, Valibot, and any Standard Schema compliant library.\n */\nexport type SafeParseSchema = {\n safeParse(\n data: unknown,\n ): { success: true } | { error: { issues: { message: string; path: (string | number)[] }[] }; success: false };\n};\n\n/** The type of values accessible at a scoped sub-form prefix. */\nexport type ScopedValues<TValues, P extends string> =\n TypeAtPath<TValues, P> extends Record<string, unknown>\n ? TypeAtPath<TValues, P> & Record<string, unknown>\n : Record<string, unknown>;\n\n/**\n * The live connection object returned by `form.connect()`.\n * Getters re-evaluate on every property access — store the object once per field, do not destructure.\n * Call `dispose()` to cancel any pending debounce timers when the field unmounts.\n */\nexport type ConnectionResult<V = unknown> = {\n [Symbol.dispose](): void;\n readonly dirty: boolean;\n /** Cancels the debounce timer owned by this specific binding. Safe to call multiple times. */\n dispose(): void;\n /** `true` after `dispose()` has been called on this binding. */\n readonly disposed: boolean;\n readonly error: string | undefined;\n onBlur(): void;\n onChange(value: V): void;\n readonly touched: boolean;\n readonly value: V;\n};\n\n/** Per-field or global validation trigger configuration. */\nexport type ConnectOptions = {\n /**\n * Debounce auto-triggered validation by this many milliseconds.\n * Each `connect()` call owns its own timer — cancelling one binding does not affect others.\n * Default: 0.\n */\n debounce?: number;\n /** Auto-mark the field as touched when it loses focus. Default: false. */\n touchOnBlur?: boolean;\n /** Validate the field automatically when it loses focus. Default: false. */\n validateOnBlur?: boolean;\n /** Validate the field automatically after each value change. Default: false. */\n validateOnChange?: boolean;\n /** Validate on change only after the field has been touched at least once. */\n validateOnTouch?: boolean;\n};\n\n// Resolves the element type of an array, or `unknown` for non-arrays.\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : unknown;\n\nexport type ArrayField<T = unknown> = {\n append(value: T): void;\n insert(index: number, value: T): void;\n move(from: number, to: number): void;\n prepend(value: T): void;\n remove(index: number): void;\n replace(index: number, value: T): void;\n swap(a: number, b: number): void;\n};\n\nexport type FieldState<V = unknown> = {\n dirty: boolean;\n error: string | undefined;\n /** `true` when `error` is not `undefined`. Convenience alias for `error !== undefined`. */\n hasError: boolean;\n touched: boolean;\n value: V;\n};\n\n/**\n * Options for registering a dynamic field. Both properties are optional;\n * omitting `defaultValue` leaves the field uninitialized and omitting `validator`\n * skips field-level validation.\n */\nexport type RegisterFieldOptions<V = unknown> = {\n /** Initial value. Also becomes the baseline, so the field starts clean. */\n defaultValue?: V;\n /** Field-level validator to attach. */\n validator?: FieldValidator<V>;\n};\n\n/**\n * A complete snapshot of form state that can be restored via `form.restore()`.\n * `store` and `baseline` keys are the flattened dot-notation paths used internally.\n * Typed with `TValues` for nominal clarity — structurally both are `Record<string, unknown>`.\n */\nexport type FormSnapshot<TValues extends Record<string, unknown> = Record<string, unknown>> = {\n readonly baseline: Partial<Record<FlatKeyOf<TValues>, unknown>>;\n readonly dirty: readonly string[];\n readonly errors: Readonly<Record<string, string>>;\n readonly store: Partial<Record<FlatKeyOf<TValues>, unknown>>;\n readonly submitCount: number;\n readonly touched: readonly string[];\n};\n\nexport type FormOptions<TValues extends Record<string, unknown> = Record<string, unknown>> = {\n /**\n * Default connect behavior applied when `connect(name, config)` omits specific options.\n * Use `ValidationModes` presets for common strategies:\n * - `ValidationModes.onSubmit` (default) — validates only on submit, no touch-on-blur\n * - `ValidationModes.onBlur` — validates each field on blur\n * - `ValidationModes.onChange` — validates each field on every change\n * - `ValidationModes.onTouched` — validates on blur first, then on every change once touched\n */\n connect?: ConnectOptions;\n /**\n * Initial form values. May be a static object or an async factory function.\n * While a factory is pending, `form.isLoading` is `true` and the form is read-only.\n */\n defaultValues?: { [K in keyof TValues]: TValues[K] } | (() => Promise<{ [K in keyof TValues]: TValues[K] }>);\n /**\n * Called when an async `defaultValues` factory rejects.\n * Use to show an error UI or retry logic. In development, a warning is also emitted via `console.warn`.\n */\n onLoadError?: (error: unknown) => void;\n /**\n * Form-level validator. Accepts either:\n * - A `FormValidator` function\n * - Any `safeParse`-compatible schema (auto-detected, works with `@vielzeug/spell`, Zod, Valibot)\n */\n validator?: FormValidator<TValues> | SafeParseSchema;\n /** Field-level validators keyed by field name or dot-notation path. */\n validators?: Partial<Record<FlatKeyOf<TValues>, FieldValidator>>;\n};\n\n/**\n * Named presets for common validation trigger strategies.\n * Pass to `createForm({ connect: ValidationModes.onBlur })`.\n *\n * - `onSubmit` — no automatic validation or touch. Clean until the user submits.\n * - `onBlur` — validates and marks touched on blur.\n * - `onChange` — validates on every keystroke (marks touched on blur).\n * - `onTouched` — validates on blur first, then on every change once touched.\n */\nexport const ValidationModes = {\n onBlur: { touchOnBlur: true, validateOnBlur: true },\n onChange: { touchOnBlur: true, validateOnChange: true },\n onSubmit: {},\n onTouched: { touchOnBlur: true, validateOnBlur: true, validateOnTouch: true },\n} as const satisfies Record<string, ConnectOptions>;\n\nexport interface Form<TValues extends Record<string, unknown> = Record<string, unknown>> {\n /** Returns a cached typed `ArrayField` helper for mutating array-valued fields. */\n array<K extends FlatKeyOf<TValues>>(name: K): ArrayField<ElementOf<TypeAtPath<TValues, K>>>;\n batch(fn: () => void): void;\n /** Clear a single field error. No-op if the field has no error. */\n clearError(name: ErrorKeyOf<TValues>): void;\n /**\n * Returns a live connection object for a field (DOM event handlers + live getters).\n * Each call to `connect()` creates a new object with its own debounce timer.\n * Call `binding.dispose()` on unmount to cancel any pending debounce timer.\n * Do not destructure: getters re-evaluate on every property access.\n */\n connect<K extends FlatKeyOf<TValues>>(name: K, config?: ConnectOptions): ConnectionResult<TypeAtPath<TValues, K>>;\n dispose(): void;\n /** Aborted when `dispose()` is called. Use to cancel async work tied to this form's lifetime. */\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n field<K extends FlatKeyOf<TValues>>(name: K): FieldState<TypeAtPath<TValues, K>>;\n get<K extends FlatKeyOf<TValues>>(name: K): TypeAtPath<TValues, K>;\n /** `true` while an async `defaultValues` factory is resolving. Mirrors `state.isLoading`. */\n readonly isLoading: boolean;\n /** `true` while a `submit()` call is in progress. Mirrors `state.isSubmitting`. */\n readonly isSubmitting: boolean;\n /**\n * Merge a partial server response into the form: updates both the store and the baseline\n * for each provided field, marking them clean. Fields not included in `partial` are untouched.\n */\n patch(partial: DeepPartial<TValues>): void;\n /**\n * Field-lifecycle namespace — use to dynamically register/remove fields and manage validators.\n *\n * @example\n * const unsub = form.fields.register('email', { defaultValue: '', validator: required });\n * form.fields.setValidator('email', (v) => v ? undefined : 'Required');\n * form.fields.remove('email');\n */\n fields: {\n /**\n * Enumerate all currently-known field paths: the deduplicated union of populated store\n * keys and keys with a registered validator (the same definition `touchAll()` uses).\n * On a scoped form, paths are relative to the scope's prefix — matching `scope.state`'s\n * relative-path convention, not `state.touchedFields`'s absolute-path convention.\n */\n list(): readonly string[];\n /**\n * Declare a dynamic field with an optional default value and validator.\n * If the field does not yet exist in the store, `defaultValue` is written to both the\n * store and the baseline so the field starts clean (not dirty).\n * Returns an **unregister** callback that calls `fields.remove()` on the same field.\n */\n register<K extends FlatKeyOf<TValues>>(\n name: K,\n options?: RegisterFieldOptions<TypeAtPath<TValues, K>>,\n ): Unsubscribe;\n /** Remove a field entirely: drops value, dirty, touched, error, and any registered validator. */\n remove(name: FlatKeyOf<TValues>): void;\n /**\n * Add, replace, or remove a field validator.\n * Removing a validator (passing `undefined`) also immediately clears any existing error for that field.\n */\n setValidator(name: FlatKeyOf<TValues>, validator?: FieldValidator): void;\n };\n /** Replace values and baseline in one operation. Aborts any in-flight validation. */\n replace(newValues: TValues): void;\n /** Restore values from the current baseline and clear errors/touched/dirty. Aborts any in-flight validation. */\n reset(): void;\n resetField(name: FlatKeyOf<TValues>): void;\n /** Replace the entire error map. Use `undefined` values to omit entries. */\n resetErrors(errors?: Partial<Record<ErrorKeyOf<TValues>, string | undefined>>): void;\n /**\n * Returns a scoped sub-form whose field paths are relative to `prefix`.\n * `scopedForm.get('city')` is equivalent to `form.get('${prefix}.city')`.\n * The scoped form shares state and lifecycle with the parent — `dispose()` on a scoped\n * form is a no-op; call `parentForm.dispose()` to tear down the whole form.\n *\n * Results are memoized — calling `scope(prefix)` multiple times with the same prefix\n * returns the same object. Safe to call on every render.\n *\n * **`state`** returns a scoped projection: `errors`, `touchedFields`, `validatingFields`,\n * `isValid`, `isDirty`, and `isTouched` reflect only fields within this prefix.\n * `isSubmitting`, `isLoading`, `isValidating`, and `submitCount` reflect the full form.\n */\n scope<P extends FlatKeyOf<TValues>>(prefix: P): Form<ScopedValues<TValues, P>>;\n set<K extends FlatKeyOf<TValues>>(name: K, value: TypeAtPath<TValues, K>, options?: SetOptions): void;\n /** Set a field error. */\n setError(name: ErrorKeyOf<TValues>, message: string): void;\n /**\n * Capture the complete form state into a snapshot that can be passed to `restore()`.\n * Useful for undo/redo, draft saving, and \"discard changes\" flows.\n */\n snapshot(): FormSnapshot<TValues>;\n /**\n * Restore the form to a previously captured snapshot. Replaces all state:\n * values, baseline, errors, touched, dirty, and submitCount.\n * Aborts any in-flight validation.\n */\n restore(snapshot: FormSnapshot<TValues>): void;\n readonly state: FormState;\n submit<TResult = void>(handler: (values: TValues) => MaybePromise<TResult>): Promise<SubmitResult<TResult>>;\n /**\n * Like `submit()` but throws a `ForgeValidationError` when validation fails instead of returning\n * `{ ok: false, ... }`. Use when you prefer a throw-based control flow.\n *\n * @throws {ForgeValidationError} when validation fails.\n * @throws {Error} when `submit()` is already in progress.\n */\n submitOrThrow<TResult = void>(handler: (values: TValues) => MaybePromise<TResult>): Promise<TResult>;\n subscribe(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe;\n /**\n * Subscribe to form state changes filtered to this scope's prefix.\n * `errors`, `touchedFields`, and `validatingFields` are remapped to relative paths;\n * all other state flags reflect the full form.\n * The listener fires only when the scoped projection changes — unrelated field mutations\n * outside this prefix are suppressed.\n * On a root form, behaves identically to `subscribe` — no prefix filtering is applied.\n */\n subscribeScoped(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe;\n subscribeField<K extends FlatKeyOf<TValues>>(\n name: K,\n listener: (state: FieldState<TypeAtPath<TValues, K>>) => void,\n options?: SubscribeOptions,\n ): Unsubscribe;\n /**\n * Iterate over form state changes via `for await`.\n * Yields the current state immediately, then once per mutation until the iterator\n * is returned or the form is disposed.\n *\n * @example\n * for await (const state of form) {\n * if (state.isSubmitting) showSpinner();\n * }\n */\n [Symbol.asyncIterator](): AsyncIterableIterator<FormState>;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Mark a field as touched. */\n touch(name: FlatKeyOf<TValues>): void;\n /** Mark all known fields (store + validators) as touched. */\n touchAll(): void;\n /** Mark a field as untouched. No-op if the field is not currently touched. */\n untouch(name: FlatKeyOf<TValues>): void;\n /** Mark all fields as untouched. */\n untouchAll(): void;\n /**\n * Validate all registered field validators and the optional form validator.\n *\n * On a scoped form: validates only the fields within the scope and does not run the\n * form-level validator. Errors and `valid` reflect only scoped fields.\n */\n validate(signal?: AbortSignal): Promise<ValidateResult>;\n /**\n * Validate a single named field. Preserves other fields' errors. Does not run the form-level validator.\n * Returns a `ValidateResult` scoped to that field — `errors` contains at most one entry.\n */\n validate(name: FlatKeyOf<TValues>, signal?: AbortSignal): Promise<ValidateResult>;\n /**\n * Validate a specific set of fields. Preserves other fields' errors. Does not run the form-level validator.\n * Returns a `ValidateResult` scoped to the requested fields.\n */\n validate(fields: FlatKeyOf<TValues>[], signal?: AbortSignal): Promise<ValidateResult>;\n /**\n * Streaming validation — yields each field result as soon as its validator resolves.\n * The final item in the stream is the form-level validator result (field: '_form'), if configured.\n *\n * @example\n * for await (const result of form.validateStream()) {\n * console.log(result.field, result.error ?? 'ok');\n * }\n */\n validateStream(signal?: AbortSignal): AsyncIterableIterator<{ error: string | undefined; field: string }>;\n values(): TValues;\n}\n"],"mappings":"AA4CA,IAAa,EAAa,QAkMb,EAAkB,CAC7B,OAAQ,CAAE,YAAa,GAAM,eAAgB,EAAK,EAClD,SAAU,CAAE,YAAa,GAAM,iBAAkB,EAAK,EACtD,SAAU,CAAC,EACX,UAAW,CAAE,YAAa,GAAM,eAAgB,GAAM,gBAAiB,EAAK,CAC9E"}
|