{"version":3,"file":"FormEngine.mjs","names":[],"sources":["../../../../../../../react-form/src/engine/FormEngine.ts"],"sourcesContent":["import events, { EventSubscription } from \"@mongez/events\";\nimport { debounce, get, merge, toInputName } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport React from \"react\";\nimport {\n  addToFormsList,\n  removeActiveForm,\n  removeFromFormsList,\n  setActiveForm,\n} from \"../active-form\";\nimport { getFormConfig } from \"../configurations\";\nimport {\n  issuePathToName,\n  isStandardSchema,\n  runStandardSchema,\n} from \"../standard-schema/adapter\";\nimport { StandardSchemaV1 } from \"../standard-schema/types\";\nimport {\n  FillOptions,\n  FormControl,\n  FormControlValues,\n  FormEventType,\n  FormInterface,\n  ValidateOn,\n} from \"../types\";\n\n/**\n * Process-wide counter used only as the LAST resort for a form id (when the\n * host passes none and React's `useId()` is unavailable). Ids are otherwise\n * supplied by the host so they stay stable and SSR-safe.\n */\nlet engineCounter = 0;\n\n/**\n * Mutable options synced into the engine each render by the host component.\n */\nexport type FormEngineOptions = {\n  id?: string;\n  defaultValue?: Record<string, any>;\n  values?: Record<string, any>;\n  schema?: StandardSchemaV1;\n  validateOn?: ValidateOn;\n  ignoreEmptyValues?: boolean;\n  focusFirstError?: boolean;\n  onSubmit?: (options: {\n    form: FormInterface;\n    event?: React.FormEvent;\n    values: any;\n    formData: FormData;\n  }) => void | Promise<any>;\n  onError?: (invalidControls: FormControl[]) => void;\n};\n\n/**\n * Platform-agnostic, **React-free** form engine — the controller object behind\n * every `<Form>` / `<NativeForm>`.\n *\n * It is a plain class (not a `React.Component`) so it can be unit-tested in\n * isolation and held in a `useRef` by the host function component. The host\n * owns rendering and the host element; the engine owns *everything else* —\n * registration, validation, value collection, dirty tracking, hydration, and\n * the submit pipeline.\n *\n * ## Mental model\n *\n * The engine never re-renders anything itself. It coordinates the form through\n * three cooperating mechanisms — keep these in mind when reading any method:\n *\n * 1. **Mutable instance state** (`formControls`, `invalidControls`,\n *    `dirtyControls`, `_isSubmitting`, …). Plain arrays/flags mutated in place.\n *    Mutating them is intentionally invisible to React; that is what keeps the\n *    library fast (registering a control or flipping validity does not re-render\n *    the tree).\n * 2. **A pub/sub event bus** (`@mongez/events`). Every event is namespaced under\n *    `form.{id}.{event}` (see {@link on} / {@link trigger}). Consumers like\n *    `useSubmitButton` / `useWatch` and the host subscribe via {@link on}; the\n *    engine emits via {@link trigger}. Per-control value changes flow on a\n *    separate key, `form.control.{controlId}.change`, owned by the control.\n * 3. **Control registration.** Each `useFormControl` builds a mutable\n *    `FormControl` object and calls {@link register} on mount / {@link unregister}\n *    on unmount. The engine reads/writes those objects directly (e.g.\n *    `control.value`, `control.validate()`); it never copies their state.\n *\n * ## Lifecycle (driven by the host component)\n *\n * ```text\n * new FormEngine(options)      // construct once, seed id + defaultValue + values + schema\n *   → activate()               // mount effect: register as the active form\n *   → setOptions(options)      // every render: re-sync scalar handlers (onSubmit/onError/…)\n *   → fill() / setDefaultValue()   // when the reactive `values` / `defaultValue` prop changes\n *   → register() / unregister()    // as controls mount / unmount\n *   → handleSubmit() / validate()  // on submit (native event or form.submit())\n *   → destroy()                // unmount effect: drop active-form registration\n * ```\n *\n * The host injects {@link submitHandler} so `form.submit()` can trigger a native\n * DOM submit on web vs. run the pipeline directly on React Native.\n */\nexport class FormEngine implements FormInterface {\n  /**\n   * Reference to the host element (HTMLFormElement on web, any on native).\n   * Assigned by the host component via its ref callback.\n   */\n  public formElement: any = null;\n\n  /**\n   * Form id.\n   */\n  protected formId: string;\n\n  /**\n   * Form event prefix.\n   */\n  protected formEventPrefix: string;\n\n  /**\n   * Form controls.\n   */\n  protected formControls: FormControl[] = [];\n\n  /**\n   * Determine whether form validation is valid.\n   */\n  protected isValidForm = true;\n\n  /**\n   * Determine form submission state.\n   */\n  protected _isSubmitting = false;\n\n  /**\n   * Determine if form is disabled.\n   */\n  protected _isDisabled = false;\n\n  /**\n   * List of invalid controls.\n   */\n  protected invalidControls: FormControl[] = [];\n\n  /**\n   * List of valid controls.\n   */\n  protected validControls: FormControl[] = [];\n\n  /**\n   * Form-level error messages with no owning control — produced by whole-form\n   * schema issues whose path maps to no control (cross-field / root errors).\n   */\n  public formErrors: React.ReactNode[] = [];\n\n  /**\n   * Dirty controls.\n   */\n  public dirtyControls: FormControl[] = [];\n\n  /**\n   * Default value (reset baseline).\n   */\n  public defaultValue: Record<string, any> | undefined;\n\n  /**\n   * Reactive hydration snapshot (nested object) — read by controls mounting\n   * after a `fill()` / reactive `values` update.\n   */\n  public hydrationValues: Record<string, any> | undefined;\n\n  /**\n   * Whole-form Standard Schema validator.\n   */\n  public schema: StandardSchemaV1 | undefined;\n\n  /**\n   * Form-level default validation trigger.\n   */\n  public validateOn: ValidateOn | undefined;\n\n  /**\n   * Whether the form has attempted submission at least once.\n   */\n  public wasSubmitted = false;\n\n  /**\n   * Form control change subscriptions, keyed by control id/name.\n   */\n  protected formControlEvents: Record<string, EventSubscription[]> = {};\n\n  /**\n   * Current form dirty state.\n   */\n  public isDirty = false;\n\n  /**\n   * Latest mutable options synced from the host component.\n   */\n  protected formOptions: FormEngineOptions;\n\n  // ───────────────────────────────────────────────────────────────────────\n  // Lifecycle — construct once, sync each render, (de)activate on mount/unmount\n  // ───────────────────────────────────────────────────────────────────────\n\n  /**\n   * Seeds the one-time state: the (stable) form id + its event prefix, the\n   * reset baseline (`defaultValue`), the initial hydration snapshot (cloned from\n   * `values` so later `fill()` merges don't mutate the caller's object), the\n   * whole-form schema, and the default validation trigger.\n   *\n   * Everything that can change between renders (the `onSubmit` / `onError`\n   * handlers, `ignoreEmptyValues`, …) is re-read from {@link setOptions}, not\n   * captured here.\n   */\n  public constructor(options: FormEngineOptions = {}) {\n    this.formOptions = options;\n    this.formId =\n      options.id || `frm-${(++engineCounter).toString(36)}`;\n    this.formEventPrefix = `form.${this.formId}`;\n    this.defaultValue = options.defaultValue;\n    this.hydrationValues = options.values\n      ? merge({}, options.values)\n      : undefined;\n    this.schema = isStandardSchema(options.schema) ? options.schema : undefined;\n    this.validateOn = options.validateOn;\n  }\n\n  /**\n   * Sync the latest scalar options/handlers from the host component. Reactive\n   * `values` / `defaultValue` identity changes are handled by the host calling\n   * `fill()` / `setDefaultValue()` explicitly.\n   */\n  public setOptions(options: FormEngineOptions) {\n    this.formOptions = options;\n    this.schema = isStandardSchema(options.schema) ? options.schema : undefined;\n    this.validateOn = options.validateOn;\n  }\n\n  /**\n   * Register the engine as the active form. Called by the host on mount.\n   */\n  public activate() {\n    setActiveForm(this);\n    addToFormsList(this);\n  }\n\n  /**\n   * Tear down active-form registration. Called by the host on unmount.\n   */\n  public destroy() {\n    removeActiveForm(this);\n    removeFromFormsList(this);\n  }\n\n  public change(name: string, value: any) {\n    const formControl = this.control(name);\n\n    if (!formControl) return;\n\n    formControl.change(value);\n  }\n\n  // ───────────────────────────────────────────────────────────────────────\n  // Validity tracking\n  //\n  // Two layers work together:\n  //  - EAGER, per-control: invalidControl()/validControl() move a single control\n  //    between the valid/invalid buckets the instant its own validation\n  //    resolves, and fire the singular `invalidControl` / `validControl` events.\n  //  - DEBOUNCED, aggregate: checkIfIsValid() coalesces a burst of per-control\n  //    updates (e.g. validating the whole form) into ONE `validControls` /\n  //    `invalidControls` event on the next tick, so a button bound to form\n  //    validity flips once, not once per field.\n  // ───────────────────────────────────────────────────────────────────────\n\n  /** Move a control into the invalid bucket and mark the form invalid. */\n  public invalidControl(formControl: FormControl) {\n    this.isValidForm = false;\n\n    this.validControls = this.validControls.filter(\n      (control) => control.id !== formControl.id\n    );\n\n    if (!this.invalidControls.includes(formControl)) {\n      this.invalidControls.push(formControl);\n    }\n\n    this.trigger(\"invalidControl\", formControl, this);\n  }\n\n  /**\n   * Move a control into the valid bucket; the form is valid again only once the\n   * invalid bucket is empty.\n   */\n  public validControl(formControl: FormControl) {\n    this.invalidControls = this.invalidControls.filter(\n      (control) => control.id !== formControl.id\n    );\n\n    if (!this.validControls.includes(formControl)) {\n      this.validControls.push(formControl);\n    }\n\n    this.isValidForm = this.invalidControls.length === 0;\n\n    this.trigger(\"validControl\", formControl, this);\n  }\n\n  /**\n   * Debounced aggregate validity check. Call it after a batch of per-control\n   * validity changes; it emits a single `validControls` / `invalidControls`\n   * event on the next tick. (A field initializer rather than a method so each\n   * engine instance owns its own debounced function.)\n   */\n  public checkIfIsValid = this._checkIfIsValid();\n\n  protected _checkIfIsValid() {\n    return debounce(() => {\n      const isValidForm = this.invalidControls.length === 0;\n\n      this.isValidForm = isValidForm;\n\n      if (this.isValidForm) {\n        this.trigger(\"validControls\", this.validControls, this);\n      } else {\n        this.trigger(\"invalidControls\", this.invalidControls, this);\n      }\n    }, 0);\n  }\n\n  /**\n   * Set the in-flight submit state and emit `submitting`. Clearing it\n   * (`submitting(false)`) *also* emits `submit` — that is the single completion\n   * signal for an async submit (see {@link handleSubmit}). Call this yourself in\n   * a sync `onSubmit`'s success/failure path to re-enable a submit button.\n   */\n  public submitting(submitting: boolean) {\n    this._isSubmitting = submitting;\n\n    this.trigger(\"submitting\", submitting, this);\n\n    if (submitting === false) {\n      this.trigger(\"submit\", submitting, this);\n    }\n  }\n\n  public disable(isDisabled = true) {\n    const controls = this.formControls;\n\n    this._isDisabled = isDisabled;\n\n    controls.forEach((control) => {\n      control.disable(isDisabled);\n    });\n\n    this.trigger(\"disable\", isDisabled, this);\n\n    return this;\n  }\n\n  public enable() {\n    return this.disable(false);\n  }\n\n  /** Whether the whole form is currently disabled (via {@link disable}). */\n  public isDisabled() {\n    return this._isDisabled;\n  }\n\n  public isSubmitting() {\n    return this._isSubmitting;\n  }\n\n  public isValid() {\n    return this.isValidForm;\n  }\n\n  public get id() {\n    return this.formId;\n  }\n\n  // ───────────────────────────────────────────────────────────────────────\n  // Events — all keyed under `form.{id}.{event}` so multiple forms never collide\n  // ───────────────────────────────────────────────────────────────────────\n\n  /**\n   * Subscribe to a form event. Returns an `EventSubscription` — call\n   * `.unsubscribe()` (typically in a `useEffect` cleanup) to detach.\n   */\n  public on(\n    event: FormEventType,\n    callback: (form: FormInterface) => void\n  ): EventSubscription {\n    return events.subscribe(`${this.formEventPrefix}.${event}`, callback);\n  }\n\n  /** Emit a form event (fire-and-forget; listener return values are ignored). */\n  public trigger(event: FormEventType, ...values: any[]) {\n    return events.trigger(`${this.formEventPrefix}.${event}`, ...values);\n  }\n\n  /**\n   * Emit a form event and collect every listener's return value in\n   * `response.results`. Used by {@link validate} for the `validating` veto: a\n   * listener returning `false` aborts validation.\n   */\n  public triggerAll(event: FormEventType, ...values: any[]) {\n    return events.triggerAll(`${this.formEventPrefix}.${event}`, ...values);\n  }\n\n  // ───────────────────────────────────────────────────────────────────────\n  // Validation pipeline\n  // ───────────────────────────────────────────────────────────────────────\n\n  /**\n   * Validate `controls` (the whole form by default) and recompute form validity.\n   *\n   * Flow:\n   *  1. Reset the valid/invalid buckets and optimistically assume valid.\n   *  2. Fire `validating` — any listener returning `false` **vetoes** the run\n   *     (the form is marked invalid and nothing else validates).\n   *  3. `await` each control's `validate()` in turn. This is what makes async\n   *     rules genuinely gate submission: a control whose rule returns a Promise\n   *     is awaited here before the form decides it is valid. Each control sorts\n   *     itself into the invalid/valid bucket via its resolved `isValid`.\n   *  4. If a whole-form {@link schema} is set, run it and map issues back to\n   *     controls (see {@link validateSchema}).\n   *  5. Emit `validation`, schedule the debounced aggregate event, and call the\n   *     `onError` handler when invalid.\n   *\n   * `validateVisible()` passes a filtered `controls` subset; schema issues for\n   * controls outside that subset are ignored so hidden fields don't fail it.\n   */\n  public async validate(controls: FormControl[] | string[] = this.formControls) {\n    // Accept control names as a convenience: validate([\"email\", \"password\"]).\n    const targetControls: FormControl[] =\n      controls.length > 0 && typeof controls[0] === \"string\"\n        ? this.controls(controls as string[])\n        : (controls as FormControl[]);\n\n    this.isValidForm = true;\n    this.validControls = [];\n    this.invalidControls = [];\n    this.formErrors = [];\n\n    const eventResponse = this.triggerAll(\"validating\", this);\n\n    const validatedInputs: FormControl[] = [];\n\n    if (eventResponse.results.includes(false)) {\n      this.isValidForm = false;\n      return validatedInputs;\n    }\n\n    for (const input of targetControls) {\n      validatedInputs.push(input);\n\n      // Awaited: a control with an async rule resolves its `isValid` before we\n      // bucket it, so async validation actually blocks the form.\n      await input.validate();\n\n      if (input.isValid === false) {\n        this.invalidControl(input);\n      } else {\n        this.validControl(input);\n      }\n    }\n\n    // Whole-form schema validation. Issues are mapped back to controls by path;\n    // issues whose control is not in the validated subset become form-level\n    // errors (so a subset validation like `validateVisible()` still won't fail\n    // on hidden fields' own messages, but a cross-field error still blocks).\n    if (this.schema) {\n      await this.validateSchema(targetControls);\n    }\n\n    this.trigger(\"validation\", this.isValidForm, validatedInputs, this);\n\n    this.checkIfIsValid();\n\n    if (!this.isValidForm) {\n      this.formOptions.onError?.(this.invalidControls);\n\n      // Opt-in: move focus to the first invalid control so the user lands on\n      // the problem without a manual scroll/focus recipe.\n      if (this.formOptions.focusFirstError) {\n        this.invalidControls[0]?.focus();\n      }\n    }\n\n    return validatedInputs;\n  }\n\n  /**\n   * Run the whole-form Standard Schema and distribute its issues. Three cases:\n   *\n   * - **Maps to a control in the validated subset** → shown on that control.\n   * - **Maps to a control OUTSIDE the subset** (e.g. a hidden wizard step during\n   *   `validateVisible()`) → ignored, so a subset validation isn't failed by\n   *   fields it deliberately skipped.\n   * - **Root / cross-field issue (empty path)** → there is no control to own it,\n   *   so it becomes a {@link formErrors} entry and blocks submission. (These\n   *   were previously dropped, letting the form submit schema-invalid.)\n   *\n   * A non-empty path matching no control is treated as a schema/form-name\n   * mismatch and ignored, so a stray path can't make the form permanently\n   * unsubmittable.\n   */\n  protected async validateSchema(controls: FormControl[]) {\n    if (!this.schema) return;\n\n    const result = await runStandardSchema(this.schema, this.values());\n\n    if (!result.issues || result.issues.length === 0) return;\n\n    for (const issue of result.issues) {\n      const name = issuePathToName(issue.path);\n\n      const control = name ? this.control(name) : null;\n\n      if (control) {\n        if (controls.includes(control)) {\n          this.applyControlError(control, issue.message);\n        }\n        continue;\n      }\n\n      if (name === \"\") {\n        this.formErrors.push(issue.message);\n        this.isValidForm = false;\n      }\n    }\n  }\n\n  /**\n   * Force a control into an invalid state with the given message.\n   */\n  protected applyControlError(control: FormControl, message: React.ReactNode) {\n    control.setError(message);\n    control.error = message;\n    control.isValid = false;\n    this.invalidControl(control);\n  }\n\n  public validateVisible() {\n    const controls = this.formControls.filter((control) => {\n      return control.isVisible();\n    });\n\n    return this.validate(controls);\n  }\n\n  // ───────────────────────────────────────────────────────────────────────\n  // Control registration — controls call these from their mount/unmount effects\n  // ───────────────────────────────────────────────────────────────────────\n\n  /**\n   * Add a control to the form and wire its change subscription.\n   *\n   * - The leading guard makes registration idempotent (a control re-running its\n   *   effect, e.g. a field-array row whose name/index changed, won't be added\n   *   twice).\n   * - The `onChange` subscription is the engine's link to per-control changes:\n   *   it keeps `dirtyControls` (and thus `isDirty`) in sync and re-broadcasts a\n   *   form-level `change` event for `useWatch` / `useFieldArray`.\n   * - The subscription is stored under the control's key so {@link unregister}\n   *   can tear it down.\n   */\n  public register(formControl: FormControl) {\n    if (this.control(formControl.id, \"id\")) return;\n\n    this.trigger(\"registering\", formControl, this);\n\n    this.formControls.push(formControl);\n\n    const event = formControl.onChange(() => {\n      if (formControl.isDirty && !this.dirtyControls.includes(formControl)) {\n        this.dirtyControls.push(formControl);\n      } else if (\n        !formControl.isDirty &&\n        this.dirtyControls.includes(formControl)\n      ) {\n        this.dirtyControls = this.dirtyControls.filter(\n          (control) => control.id !== formControl.id\n        );\n      }\n\n      this.setIsDirty(this.dirtyControls.length > 0);\n\n      // Form-level change broadcast — consumed by `useWatch`.\n      this.trigger(\"change\", formControl, this);\n    });\n\n    this.formControlEvents[formControl.id || formControl.name] = [\n      ...(this.formControlEvents[formControl.id || formControl.name] || []),\n      event,\n    ];\n\n    this.trigger(\"register\", formControl, this);\n  }\n\n  /**\n   * Remove a control and undo everything {@link register} set up: notify the\n   * control (`unregister`), drop it from `formControls` and the invalid bucket,\n   * unsubscribe its change listener, and recompute dirty/validity so the form\n   * doesn't stay blocked by a control that no longer exists.\n   */\n  public unregister(formControl: FormControl) {\n    formControl.unregister();\n\n    const formControlIndex = this.formControls.findIndex(\n      (input) => input.id === formControl.id\n    );\n\n    if (formControlIndex === -1) return;\n\n    this.formControls.splice(formControlIndex, 1);\n\n    // Drop the control from BOTH validity buckets so an unmounted control's\n    // object (and its DOM refs / closures) isn't retained until the next full\n    // validate(). The valid bucket was previously only cleared wholesale by\n    // validate(), so a valid-then-unmounted control leaked.\n    this.invalidControls = this.invalidControls.filter(\n      (control) => control.id !== formControl.id\n    );\n    this.validControls = this.validControls.filter(\n      (control) => control.id !== formControl.id\n    );\n\n    const formControlKey = formControl.id || formControl.name;\n\n    const controlEvents = this.formControlEvents[formControlKey];\n\n    if (controlEvents) {\n      controlEvents.forEach((event) => {\n        event.unsubscribe();\n      });\n\n      delete this.formControlEvents[formControlKey];\n\n      if (this.dirtyControls.includes(formControl)) {\n        this.dirtyControls = this.dirtyControls.filter(\n          (control) => control.id !== formControl.id\n        );\n\n        this.setIsDirty(this.dirtyControls.length > 0);\n      }\n    }\n\n    this.checkIfIsValid();\n\n    this.trigger(\"unregister\", formControl, this);\n  }\n\n  protected setIsDirty(isDirty: boolean) {\n    this.isDirty = isDirty;\n\n    this.trigger(\"dirty\", isDirty, this);\n  }\n\n  public control(\n    value: string,\n    getBy: \"name\" | \"id\" = \"name\"\n  ): FormControl | null {\n    return this.formControls.find((input) => input[getBy] === value) || null;\n  }\n\n  /**\n   * Restore the form to its pristine state: each control resets to its\n   * `initialValue` (the reset baseline — see {@link getResetBaseline}) and\n   * clears its error/dirty/touched flags, then the form-level flags reset.\n   * Brackets the work with `resetting` (before) and `reset` (after) events.\n   *\n   * Pass `values` to reset to a NEW baseline (e.g. after saving an edit form):\n   * the new values are merged into `defaultValue` and become each control's\n   * reset target, so subsequent resets restore to them too.\n   */\n  public reset(values?: Record<string, any>) {\n    this.trigger(\"resetting\", this);\n\n    if (values !== undefined) {\n      this.defaultValue = merge(this.defaultValue ?? {}, values);\n\n      for (const control of this.formControls) {\n        const baseline = get(this.defaultValue, control.name);\n        if (baseline !== undefined) control.initialValue = baseline;\n      }\n    }\n\n    this.formControls.forEach((input) => {\n      input.reset();\n    });\n\n    this.isValidForm = true;\n    this._isSubmitting = false;\n    this._isDisabled = false;\n    this.wasSubmitted = false;\n    this.formErrors = [];\n\n    this.trigger(\"reset\", this);\n\n    return this;\n  }\n\n  public resetErrors() {\n    this.formControls.forEach((formControl) => {\n      formControl.setError(null);\n      formControl.isValid = null;\n    });\n\n    this.invalidControls = [];\n    // Clear the valid bucket too — otherwise stale/unmounted controls linger\n    // here until a full re-validate.\n    this.validControls = [];\n    this.formErrors = [];\n    this.isValidForm = true;\n\n    return this;\n  }\n\n  // ───────────────────────────────────────────────────────────────────────\n  // Values & hydration\n  //\n  // Two distinct concepts that are easy to conflate:\n  //  - DISPLAY seed (getInitialValue): what a control shows when it mounts.\n  //    Live-loaded `values` (hydrationValues) win here so edit forms render the\n  //    record.\n  //  - RESET baseline (getResetBaseline): what `form.reset()` restores to. Only\n  //    `defaultValue` counts — never the hydration snapshot — so loading a\n  //    record never silently becomes the \"reset\" target.\n  // Controls read the display seed for their initial value and the reset\n  // baseline for `formControl.initialValue`.\n  // ───────────────────────────────────────────────────────────────────────\n\n  /**\n   * Resolve the seed (display) value for a control name: the reactive hydration\n   * snapshot wins over the reset baseline.\n   */\n  public getInitialValue(name: string): any {\n    if (this.hydrationValues !== undefined) {\n      const value = get(this.hydrationValues, name);\n      if (value !== undefined) return value;\n    }\n\n    if (this.defaultValue !== undefined) {\n      return get(this.defaultValue, name);\n    }\n\n    return undefined;\n  }\n\n  /**\n   * Resolve the **reset baseline** for a control name — from `defaultValue`\n   * only, never the hydration snapshot. `form.reset()` restores controls to\n   * this, so live-loaded `values` never become the reset target.\n   */\n  public getResetBaseline(name: string): any {\n    if (this.defaultValue === undefined) return undefined;\n\n    return get(this.defaultValue, name);\n  }\n\n  /**\n   * Bulk-write values onto mounted controls and seed later-mounting controls.\n   * Does not change the reset baseline (`defaultValue`).\n   */\n  public fill(values: Record<string, any>, options: FillOptions = {}) {\n    const { dirty = false, validate = false } = options;\n\n    this.hydrationValues = merge(this.hydrationValues ?? {}, values);\n\n    for (const control of this.formControls) {\n      const value = get(values, control.name);\n\n      if (value === undefined) continue;\n\n      control.change(value, { dirty, validate, updateState: true });\n    }\n\n    // Notify hydration listeners (useWatch, useFieldArray) even when no control\n    // matched yet — e.g. a field array that needs to grow its rows to match a\n    // freshly-loaded array.\n    this.trigger(\"change\", undefined, this);\n\n    return this;\n  }\n\n  public setValues(values: Record<string, any>, options: FillOptions = {}) {\n    return this.fill(values, options);\n  }\n\n  /**\n   * Update the reset baseline. Pristine (non-dirty) controls re-hydrate to the\n   * new baseline; dirty controls keep the user's edits.\n   */\n  public setDefaultValue(defaultValue: Record<string, any> | undefined) {\n    this.defaultValue = defaultValue;\n\n    if (!defaultValue) return this;\n\n    for (const control of this.formControls) {\n      if (control.isDirty) continue;\n\n      const value = get(defaultValue, control.name);\n\n      if (value === undefined) continue;\n\n      control.initialValue = value;\n      control.change(value, { dirty: false, validate: false, updateState: true });\n    }\n\n    return this;\n  }\n\n  public setErrors(errors: Record<string, React.ReactNode>) {\n    for (const name in errors) {\n      const control = this.control(name);\n\n      if (!control) continue;\n\n      this.applyControlError(control, errors[name]);\n    }\n\n    this.checkIfIsValid();\n\n    if (this.invalidControls.length > 0) {\n      this.trigger(\"invalidControls\", this.invalidControls, this);\n    }\n\n    return this;\n  }\n\n  /** Current value of a single control by name (live, not collected). */\n  public value(formControlName: string): any {\n    return this.control(formControlName, \"name\")?.value;\n  }\n\n  /**\n   * The collected form values as a nested object: takes the flat dot-notation\n   * map from {@link collectValues} and expands it (`user.address.city` →\n   * `{ user: { address: { city } } }`, numeric segments → arrays). This is what\n   * `onSubmit` receives as `values`.\n   */\n  public values(formControlNames: string[] = []) {\n    return createNestedObjectFromDotNotation(\n      this.collectValues(formControlNames)\n    );\n  }\n\n  public shouldIgnoreEmptyValues() {\n    return (\n      this.formOptions.ignoreEmptyValues !== undefined\n        ? this.formOptions.ignoreEmptyValues\n        : getFormConfig(\"ignoreEmptyValues\", false)\n    ) as boolean;\n  }\n\n  /**\n   * Collect a FLAT `{ dotNotationName: value }` map from the registered\n   * controls — the raw material {@link values} nests into objects/arrays.\n   *\n   * Per control: skip the unnamed and the non-collectable (disabled, unchecked\n   * boxes, …); optionally drop empties when `ignoreEmptyValues` is on; then\n   * merge into the map. The merge collapses repeats into an array — if a name\n   * already has a value (or the control is `multiple`), the slot is promoted to\n   * an array and subsequent values are pushed. That is how N inputs sharing one\n   * `name` (e.g. a checkbox group) become a single array value.\n   */\n  public collectValues(formControlNames: string[] = []) {\n    const formControls = this.controls(formControlNames);\n\n    const values: FormControlValues = {};\n\n    const ignoreEmptyValues = this.shouldIgnoreEmptyValues();\n\n    for (const formControl of formControls) {\n      const name = formControl.name;\n      if (!name || !formControl.isCollectable()) continue;\n\n      const value = formControl.collectValue();\n\n      if (\n        ignoreEmptyValues &&\n        ([null, undefined, \"\"].includes(value) ||\n          (Array.isArray(value) && value.length === 0))\n      )\n        continue;\n\n      if (\n        (values[name] || formControl.multiple) &&\n        !Array.isArray(values[name]) &&\n        !Array.isArray(value)\n      ) {\n        values[name] = values[name] ? [values[name]] : [];\n      }\n\n      if (Array.isArray(values[name])) {\n        values[name].push(value);\n      } else {\n        values[name] = value;\n      }\n    }\n\n    return values;\n  }\n\n  /**\n   * The collected values as a `FormData` for `multipart/form-data` submits.\n   * Mirrors the nested shape using PHP/Rails-style bracket keys on the wire:\n   * arrays as `name[]`, plain objects as `name[key]`.\n   */\n  public formData(): FormData {\n    const formData: FormData = new FormData();\n\n    const values = this.collectValues();\n\n    for (const name in values) {\n      const value = values[name];\n      const formControlName = toInputName(name);\n\n      if (Array.isArray(value)) {\n        for (const item of value) {\n          formData.append(`${formControlName}[]`, item);\n        }\n        continue;\n      } else if (isPlainObject(value)) {\n        for (const key in value) {\n          formData.append(`${formControlName}[${key}]`, value[key]);\n        }\n        continue;\n      }\n\n      formData.append(formControlName, value);\n    }\n\n    return formData;\n  }\n\n  public controls(formControls: string[] = []): FormControl[] {\n    if (formControls?.length === 0) return this.formControls;\n\n    return this.formControls.filter((formControl) =>\n      formControls.includes(formControl.name)\n    );\n  }\n\n  // ───────────────────────────────────────────────────────────────────────\n  // Submit pipeline\n  //\n  // Two entry points converge on handleSubmit():\n  //  - Web: form.submit() → submitHandler() dispatches a native submit event →\n  //    the host's onSubmit listener calls handleSubmit(event).\n  //  - Native / programmatic: submitHandler() (or the host) calls handleSubmit()\n  //    directly — there is no DOM event to dispatch.\n  // ───────────────────────────────────────────────────────────────────────\n\n  /**\n   * Manually submit the form. Delegates to the host-injected\n   * {@link submitHandler} because only the host knows how to dispatch a native\n   * submit on web vs. run the pipeline directly on React Native.\n   */\n  public submit() {\n    this.submitHandler?.();\n  }\n\n  /**\n   * Submit trigger injected by the host component (see {@link submit}).\n   */\n  public submitHandler: (() => void) | undefined;\n\n  /**\n   * The shared submit pipeline.\n   *\n   * Flow:\n   *  1. Mark `wasSubmitted` (drives `validateOn: \"submit\"` revalidation).\n   *  2. `await validate()` — async rules block here.\n   *  3. Bail if invalid, or if a submit is already in flight (re-entrancy guard).\n   *  4. With no `onSubmit`, emit `submit` immediately and stop.\n   *  5. Otherwise flip submitting on and invoke `onSubmit` with lazy `values` /\n   *     `formData` getters (re-collected on access).\n   *     - **Async** `onSubmit`: defer clearing the submitting state AND the\n   *       `submit` completion event until the returned promise settles, so\n   *       `submit` fires exactly once, after the work is done. (`submitting(false)`\n   *       is itself what emits `submit` here — see {@link submitting}.)\n   *     - **Sync / void** `onSubmit`: completion is immediate; the caller owns\n   *       clearing the submitting state.\n   *     - A synchronous throw clears the submitting state and re-throws.\n   */\n  public async handleSubmit(event?: React.FormEvent) {\n    this.wasSubmitted = true;\n\n    await this.validate();\n\n    if (this.isValidForm === false) return;\n\n    if (this.isSubmitting()) return;\n\n    const onSubmit = this.formOptions.onSubmit;\n\n    if (!onSubmit) {\n      // Nothing to run — emit the completion event immediately.\n      this.trigger(\"submit\", this);\n      return;\n    }\n\n    this.submitting(true);\n\n    // eslint-disable-next-line @typescript-eslint/no-this-alias\n    const form = this;\n\n    let result: void | Promise<any>;\n\n    try {\n      result = onSubmit({\n        form: this,\n        event,\n        get values() {\n          return form.values();\n        },\n        get formData() {\n          return form.formData();\n        },\n      });\n    } catch (error) {\n      this.submitting(false);\n      throw error;\n    }\n\n    if (result && typeof (result as any).then === \"function\") {\n      // Async submit: defer the `submit` completion event (and clearing the\n      // submitting state) until the promise settles, so `\"submit\"` fires once,\n      // after the work is actually done.\n      Promise.resolve(result).then(\n        () => this.submitting(false),\n        () => this.submitting(false)\n      );\n      return;\n    }\n\n    // Sync/void submit: completion is immediate.\n    this.trigger(\"submit\", this);\n  }\n}\n\n// Segments that would let a dotted field name reach `Object.prototype` (or\n// any other prototype) and pollute every plain object in the runtime.\n// Writing through them is always rejected. Kept local/standalone (no\n// cross-package dependency) — mirrors `@mongez/reinforcements`'s\n// `isForbiddenKey` guard.\nfunction isForbiddenKey(key: string): boolean {\n  return key === \"__proto__\" || key === \"constructor\" || key === \"prototype\";\n}\n\n// Numeric segments above this are treated as plain object keys rather than\n// array indices. Field names are attacker-controlled in schema/CMS-driven\n// forms; without this cap a name like `items.4000000000.x` would create a\n// sparse array with a `length` in the billions, blowing up memory/CPU the\n// first time a consumer iterates or serializes the collected values.\nconst MAX_ARRAY_INDEX = 10000;\n\nfunction isArrayIndexSegment(segment: string | undefined): boolean {\n  if (!segment) return false;\n\n  const index = Number(segment);\n\n  return !isNaN(index) && index <= MAX_ARRAY_INDEX;\n}\n\n// create a function that receives an object\n// each key is a dot notation syntax\n// return an object with nested objects\n// if the key is a dot notation syntax, then create a nested object\n// if a segment of the key is a number, then create an array\n// if the key is a number, then create an array\n// i.e name.0.text => { name: [{ text: 'value' }] }\n// name.firstName => { name: { firstName: 'value' } }\n// name.addresses.0.city => { name: { addresses: [{ city: 'value' }] } }\nexport function createNestedObjectFromDotNotation(object: any) {\n  const result: any = {};\n\n  for (const key in object) {\n    const value = object[key];\n\n    if (key.includes(\".\")) {\n      const nestedName = key.split(\".\");\n      const nestedNameLength = nestedName.length;\n\n      // Never write through/onto a prototype-pollution vector segment. Checked\n      // up front so a blocked key doesn't leave a partial container (e.g. the\n      // `a` in `a.__proto__.x`) behind on `result`.\n      if (nestedName.some(isForbiddenKey)) continue;\n\n      let nestedObject = result;\n\n      for (let i = 0; i < nestedNameLength; i++) {\n        const nestedNamePart = nestedName[i];\n        const isLastSegment = i === nestedNameLength - 1;\n        const nextSegment = nestedName[i + 1];\n\n        if (isLastSegment) {\n          // Always assign the leaf — even a falsy value (0, \"\", false). The old\n          // truthiness guard treated falsy existing values as \"absent\" and\n          // could overwrite them with a container.\n          nestedObject[nestedNamePart] = value;\n          break;\n        }\n\n        // Descending: ensure an object/array container exists. If a primitive\n        // already sits here (a leaf/parent name collision, e.g. both `a` and\n        // `a.b`), replace it with a container instead of crashing on the next\n        // property write. Existing containers are preserved.\n        const existing = nestedObject[nestedNamePart];\n\n        if (typeof existing !== \"object\" || existing === null) {\n          nestedObject[nestedNamePart] = isArrayIndexSegment(nextSegment)\n            ? []\n            : {};\n        }\n\n        nestedObject = nestedObject[nestedNamePart];\n      }\n    } else if (!isForbiddenKey(key)) {\n      result[key] = value;\n    }\n  }\n\n  return result;\n}\n"],"mappings":"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"}