{"version":3,"file":"index.mjs","names":[],"sources":["../src/isPromise.ts","../src/mapValues.ts","../src/createForm.ts","../src/createField.ts","../src/useMemoOne.ts","../src/useForm.ts","../src/useField.ts"],"sourcesContent":["export function isPromise<T, S>(\n  value: PromiseLike<T> | S\n): value is PromiseLike<T> {\n  return !!(\n    value &&\n    typeof value === \"object\" &&\n    \"then\" in value &&\n    typeof value.then === \"function\"\n  );\n}\n","export function mapValues<T, R>(\n  object: Record<string, T>,\n  callbackFn: (value: T) => R\n) {\n  return Object.fromEntries(\n    Object.entries(object).map(([key, value]) => {\n      return [key, callbackFn(value)];\n    })\n  );\n}\n","import { action, observable, runInAction } from \"mobx\";\nimport { isPromise } from \"./isPromise\";\nimport type { Field } from \"./createField\";\nimport { mapValues } from \"./mapValues\";\n\nexport type OnSubmitArg = {\n  fields: Record<string, Field<unknown>>;\n  rawValues: Record<string, unknown>;\n  values: Record<string, unknown>;\n};\n\nexport type OnSubmitFn = (props: OnSubmitArg) => unknown;\n\nexport type CreateFormArgs = {\n  onSubmit: OnSubmitFn;\n};\n\nexport type Form = ReturnType<typeof createForm>;\n\nexport function createForm({ onSubmit }: CreateFormArgs) {\n  const fields = observable({}) as Record<string, Field<unknown>>;\n\n  const state = observable({\n    isSubmitting: false,\n    valuesAtLastSubmit: undefined as undefined | string,\n    submitCount: 0,\n  });\n\n  const computed = observable({\n    get isDirty() {\n      return Object.values(fields).some((field) => field.computed.isDirty);\n    },\n    get errorList() {\n      return Object.values(fields)\n        .map((field) => field.computed.error)\n        .filter((error) => error !== undefined);\n    },\n    get isError() {\n      return Object.values(fields).some((field) => !!field.computed.error);\n    },\n    get isValid() {\n      return !this.isError;\n    },\n    get valueList() {\n      return String(Object.values(fields).map((field) => field.state.value));\n    },\n\n    get isChangedSinceLastSubmit() {\n      if (state.submitCount === 0) return this.isDirty;\n      return this.valueList !== state.valuesAtLastSubmit;\n    },\n  });\n\n  const actions = {\n    add(field: Field<unknown>) {\n      fields[field.state.id] = field;\n    },\n\n    submit: action(function submit() {\n      state.isSubmitting = true;\n      state.submitCount++;\n      state.valuesAtLastSubmit = computed.valueList;\n\n      for (const fieldId in fields) {\n        const field = fields[fieldId];\n        field.state.wasEverFocused = true;\n        field.state.wasEverBlurred = true;\n      }\n\n      if (computed.isError) {\n        state.isSubmitting = false;\n        return;\n      }\n\n      const maybePromise = onSubmit({\n        fields,\n        rawValues: mapValues(fields, (field) => field.state.value),\n        values: mapValues(fields, (field) => field.computed.parsed),\n      });\n\n      if (isPromise(maybePromise)) {\n        return Promise.resolve(maybePromise).finally(() => {\n          runInAction(() => {\n            state.isSubmitting = false;\n          });\n        });\n      }\n\n      runInAction(() => {\n        state.isSubmitting = false;\n      });\n\n      return maybePromise;\n    }),\n  };\n\n  return { fields, state, computed, actions };\n}\n","import { action, observable } from \"mobx\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nexport type ValidationFn<ValueType, ParsedType = ValueType> = (\n  value: ValueType\n) =>\n  | { error?: undefined; parsed: ParsedType }\n  | { error: Error | string; parsed?: undefined };\n\ntype YupLikeSchema<ParsedType> = {\n  validateSync(\n    value: unknown,\n    options?: { abortEarly?: boolean }\n  ): ParsedType;\n};\n\nexport type ValidationSchema<ParsedType = unknown> =\n  | StandardSchemaV1<unknown, ParsedType>\n  | YupLikeSchema<ParsedType>;\n\nexport type InferParsed<Schema, Fallback> =\n  Schema extends StandardSchemaV1<unknown, infer Out>\n    ? Out\n    : Schema extends YupLikeSchema<infer P>\n      ? P\n      : Fallback;\n\nexport type CreateFieldArgs<\n  ValueType,\n  ParsedType = ValueType,\n  Schema extends ValidationSchema<ParsedType> | undefined =\n    | ValidationSchema<ParsedType>\n    | undefined,\n> = {\n  id: string;\n  initialValue: ValueType;\n  initialError?: string | undefined;\n  form: {\n    actions: {\n      add(field: Field<any, any>): void;\n      submit(): unknown;\n    };\n  };\n} & (\n  | { validate?: undefined; validationSchema?: undefined }\n  | {\n      validate: ValidationFn<ValueType, ParsedType>;\n      validationSchema?: undefined;\n    }\n  | { validate?: undefined; validationSchema: Schema }\n);\n\nfunction isStandardSchema(schema: unknown): schema is StandardSchemaV1 {\n  return (\n    typeof schema === \"object\" && schema !== null && \"~standard\" in schema\n  );\n}\n\nfunction isYupLikeSchema<T>(schema: unknown): schema is YupLikeSchema<T> {\n  return (\n    typeof schema === \"object\" &&\n    schema !== null &&\n    \"validateSync\" in schema &&\n    typeof (schema as { validateSync: unknown }).validateSync === \"function\"\n  );\n}\n\nexport function createField<\n  ValueType,\n  Schema extends ValidationSchema<unknown> | undefined = undefined,\n  ParsedType = InferParsed<Schema, ValueType>,\n>(\n  args: CreateFieldArgs<\n    ValueType,\n    ParsedType,\n    Schema extends ValidationSchema<ParsedType> ? Schema : undefined\n  >\n): Field<ValueType, ParsedType> {\n  const { id, initialValue, initialError, form } = args;\n  const validate = args.validate;\n  const validationSchema = args.validationSchema as\n    | ValidationSchema<ParsedType>\n    | undefined;\n\n  const runValidation = getValidateFunction<ValueType, ParsedType>(\n    validate,\n    validationSchema\n  );\n\n  const state = observable({\n    id,\n    errorOverride: initialError,\n    value: initialValue,\n    isFocused: false,\n    wasEverFocused: false,\n    wasEverBlurred: false,\n  });\n\n  const computed = observable({\n    get parsed(): ParsedType | undefined {\n      const result = runValidation(state.value);\n      if (result.error) return undefined;\n      return result.parsed;\n    },\n\n    get isDirty() {\n      return JSON.stringify(state.value) !== JSON.stringify(initialValue);\n    },\n\n    get error(): string | undefined {\n      const { error } = runValidation(state.value);\n\n      if (state.errorOverride) {\n        return state.errorOverride;\n      }\n\n      if (error instanceof Error && error.name === \"ValidationError\") {\n        const msg: unknown = (error as { message: unknown }).message;\n        if (\n          msg !== null &&\n          typeof msg === \"object\" &&\n          \"value\" in msg &&\n          (msg as { value: unknown }).value !== undefined\n        ) {\n          return String((msg as { value: unknown }).value);\n        }\n        if (typeof msg === \"string\") return msg;\n        return String(error);\n      }\n\n      if (error instanceof Error) {\n        return error.message;\n      }\n\n      return error;\n    },\n\n    get ifWasEverFocusedThenError(): string | undefined {\n      if (!state.wasEverFocused) return undefined;\n      if (!computed.error) return undefined;\n      return String(computed.error);\n    },\n\n    get ifWasEverBlurredThenError(): string | undefined {\n      if (!state.wasEverBlurred) return undefined;\n      if (!computed.error) return undefined;\n      return String(computed.error);\n    },\n  });\n\n  const actions = {\n    onFocus: action(function onFocus() {\n      state.isFocused = true;\n      state.wasEverFocused = true;\n    }),\n\n    onBlur: action(function onBlur() {\n      state.isFocused = false;\n      state.wasEverBlurred = true;\n    }),\n\n    onChange: action(function onChange(value: ValueType) {\n      if (state.errorOverride) state.errorOverride = undefined;\n      state.value = value;\n    }),\n\n    setError: action(function setError(value: string | undefined) {\n      state.errorOverride = value;\n    }),\n  };\n\n  const field: Field<ValueType, ParsedType> = { state, computed, actions };\n\n  form.actions.add(field);\n\n  return field;\n}\n\nfunction getValidateFunction<ValueType, ParsedType>(\n  validate: ValidationFn<ValueType, ParsedType> | undefined,\n  validationSchema: ValidationSchema<ParsedType> | undefined\n): ValidationFn<ValueType, ParsedType> {\n  if (validate) return validate;\n\n  if (validationSchema) {\n    // Prefer Yup's `validateSync` when available — it's guaranteed sync,\n    // while Standard Schema's `validate` may return a Promise (Yup itself\n    // implements Standard Schema in async mode).\n    if (isYupLikeSchema<ParsedType>(validationSchema)) {\n      const schema = validationSchema;\n      return function validateWithYup(value: ValueType) {\n        try {\n          const parsed = schema.validateSync(value, { abortEarly: true });\n          return { parsed, error: undefined };\n        } catch (error) {\n          if (error instanceof Error && error.name === \"ValidationError\") {\n            return { parsed: undefined, error };\n          }\n          throw error;\n        }\n      };\n    }\n\n    if (isStandardSchema(validationSchema)) {\n      const schema = validationSchema;\n      return function validateWithStandardSchema(value: ValueType) {\n        const result = schema[\"~standard\"].validate(value);\n        if (result instanceof Promise) {\n          throw new TypeError(\n            \"mobx-easy-form: Standard Schema async validation is not supported. Use a synchronous schema.\"\n          );\n        }\n        if (result.issues) {\n          const message = result.issues[0]?.message ?? \"Invalid\";\n          return { parsed: undefined, error: message };\n        }\n        return { parsed: result.value as ParsedType, error: undefined };\n      };\n    }\n\n    throw new TypeError(\n      \"mobx-easy-form: validationSchema must implement Standard Schema or expose a `validateSync` method (Yup-like).\"\n    );\n  }\n\n  return function passthrough(value: ValueType) {\n    return { parsed: value as unknown as ParsedType, error: undefined };\n  };\n}\n\nexport interface Field<ValueType, ParsedType = ValueType> {\n  state: {\n    id: string;\n    errorOverride: undefined | string;\n    value: ValueType;\n    isFocused: boolean;\n    wasEverFocused: boolean;\n    wasEverBlurred: boolean;\n  };\n  computed: {\n    readonly parsed: ParsedType | undefined;\n    readonly isDirty: boolean;\n    readonly error: undefined | string;\n    readonly ifWasEverFocusedThenError: undefined | string;\n    readonly ifWasEverBlurredThenError: undefined | string;\n  };\n  actions: {\n    onFocus(): void;\n    onChange(value: ValueType): void;\n    onBlur(): void;\n    setError(value: string | undefined): void;\n  };\n}\n","import { useRef } from \"react\";\n\nfunction areInputsEqual(\n  next: ReadonlyArray<unknown>,\n  prev: ReadonlyArray<unknown>\n) {\n  if (next.length !== prev.length) return false;\n  for (let i = 0; i < next.length; i++) {\n    if (next[i] !== prev[i]) return false;\n  }\n  return true;\n}\n\nexport function useMemoOne<T>(\n  factory: () => T,\n  inputs: ReadonlyArray<unknown> = []\n): T {\n  const cache = useRef<{ inputs: ReadonlyArray<unknown>; value: T } | null>(\n    null\n  );\n\n  if (cache.current === null || !areInputsEqual(inputs, cache.current.inputs)) {\n    cache.current = { inputs, value: factory() };\n  }\n\n  return cache.current.value;\n}\n","import { useCallback, useLayoutEffect, useRef } from \"react\";\nimport { useMemoOne } from \"./useMemoOne\";\nimport { createForm, type CreateFormArgs, type Form } from \"./createForm\";\n\nfunction useEvent<Args extends unknown[], ReturnValue>(\n  handler: (...args: Args) => ReturnValue,\n) {\n  const handlerRef = useRef<(...args: Args) => ReturnValue>(handler);\n\n  useLayoutEffect(() => {\n    handlerRef.current = handler;\n  });\n\n  return useCallback((...args: Args) => handlerRef.current(...args), []);\n}\n\nexport function useForm(\n  args: CreateFormArgs,\n  deps: ReadonlyArray<unknown> = [],\n): Form {\n  const onSubmit = useEvent(args.onSubmit);\n  return useMemoOne(() => createForm({ ...args, onSubmit }), deps);\n}\n","import { useMemoOne } from \"./useMemoOne\";\nimport {\n  createField,\n  type CreateFieldArgs,\n  type Field,\n  type InferParsed,\n  type ValidationSchema,\n} from \"./createField\";\n\nexport function useField<\n  ValueType,\n  Schema extends ValidationSchema<unknown> | undefined = undefined,\n  ParsedType = InferParsed<Schema, ValueType>,\n>(\n  args: CreateFieldArgs<\n    ValueType,\n    ParsedType,\n    Schema extends ValidationSchema<ParsedType> ? Schema : undefined\n  >,\n  deps: ReadonlyArray<unknown> = [],\n): Field<ValueType, ParsedType> {\n  return useMemoOne(() => createField(args), deps) as Field<\n    ValueType,\n    ParsedType\n  >;\n}\n"],"mappings":"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"}