@vielzeug/forge 1.4.0 → 2.0.1

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.
Files changed (127) hide show
  1. package/README.md +21 -53
  2. package/dist/_utils.cjs +1 -2
  3. package/dist/_utils.d.ts +1 -10
  4. package/dist/_utils.d.ts.map +1 -1
  5. package/dist/_utils.js +2 -33
  6. package/dist/adapters/form-data.cjs +1 -1
  7. package/dist/adapters/form-data.cjs.map +1 -1
  8. package/dist/adapters/form-data.d.ts.map +1 -1
  9. package/dist/adapters/form-data.js +5 -5
  10. package/dist/adapters/form-data.js.map +1 -1
  11. package/dist/core/path.cjs +2 -0
  12. package/dist/core/path.cjs.map +1 -0
  13. package/dist/core/path.d.ts +22 -0
  14. package/dist/core/path.d.ts.map +1 -0
  15. package/dist/core/path.js +125 -0
  16. package/dist/core/path.js.map +1 -0
  17. package/dist/devtools.cjs +1 -1
  18. package/dist/devtools.cjs.map +1 -1
  19. package/dist/devtools.d.ts +3 -36
  20. package/dist/devtools.d.ts.map +1 -1
  21. package/dist/devtools.js +6 -24
  22. package/dist/devtools.js.map +1 -1
  23. package/dist/dom.cjs +2 -0
  24. package/dist/dom.cjs.map +1 -0
  25. package/dist/dom.d.ts +9 -0
  26. package/dist/dom.d.ts.map +1 -0
  27. package/dist/dom.js +24 -0
  28. package/dist/dom.js.map +1 -0
  29. package/dist/errors.cjs +1 -1
  30. package/dist/errors.cjs.map +1 -1
  31. package/dist/errors.d.ts +1 -3
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +1 -6
  34. package/dist/errors.js.map +1 -1
  35. package/dist/forge.cjs +1 -1
  36. package/dist/forge.cjs.map +1 -1
  37. package/dist/forge.iife.js +1 -1
  38. package/dist/forge.iife.js.map +1 -1
  39. package/dist/forge.js +1 -1
  40. package/dist/forge.js.map +1 -1
  41. package/dist/form.cjs +1 -1
  42. package/dist/form.cjs.map +1 -1
  43. package/dist/form.d.ts +3 -5
  44. package/dist/form.d.ts.map +1 -1
  45. package/dist/form.js +223 -58
  46. package/dist/form.js.map +1 -1
  47. package/dist/index.cjs +1 -1
  48. package/dist/index.js +3 -4
  49. package/dist/spell.cjs +2 -0
  50. package/dist/spell.cjs.map +1 -0
  51. package/dist/spell.d.ts +5 -0
  52. package/dist/spell.d.ts.map +1 -0
  53. package/dist/spell.js +57 -0
  54. package/dist/spell.js.map +1 -0
  55. package/dist/types.d.ts +70 -392
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/vault.cjs +2 -0
  58. package/dist/vault.cjs.map +1 -0
  59. package/dist/vault.d.ts +10 -0
  60. package/dist/vault.d.ts.map +1 -0
  61. package/dist/vault.js +14 -0
  62. package/dist/vault.js.map +1 -0
  63. package/package.json +49 -13
  64. package/dist/_dev.cjs +0 -2
  65. package/dist/_dev.cjs.map +0 -1
  66. package/dist/_dev.js +0 -12
  67. package/dist/_dev.js.map +0 -1
  68. package/dist/_utils.cjs.map +0 -1
  69. package/dist/_utils.js.map +0 -1
  70. package/dist/adapters/validators.cjs +0 -2
  71. package/dist/adapters/validators.cjs.map +0 -1
  72. package/dist/adapters/validators.d.ts +0 -44
  73. package/dist/adapters/validators.d.ts.map +0 -1
  74. package/dist/adapters/validators.js +0 -20
  75. package/dist/adapters/validators.js.map +0 -1
  76. package/dist/core/array.cjs +0 -2
  77. package/dist/core/array.cjs.map +0 -1
  78. package/dist/core/array.d.ts +0 -10
  79. package/dist/core/array.d.ts.map +0 -1
  80. package/dist/core/array.js +0 -77
  81. package/dist/core/array.js.map +0 -1
  82. package/dist/core/asyncQueue.cjs +0 -2
  83. package/dist/core/asyncQueue.cjs.map +0 -1
  84. package/dist/core/asyncQueue.d.ts +0 -22
  85. package/dist/core/asyncQueue.d.ts.map +0 -1
  86. package/dist/core/asyncQueue.js +0 -67
  87. package/dist/core/asyncQueue.js.map +0 -1
  88. package/dist/core/context.cjs +0 -2
  89. package/dist/core/context.cjs.map +0 -1
  90. package/dist/core/context.d.ts +0 -7
  91. package/dist/core/context.d.ts.map +0 -1
  92. package/dist/core/context.js +0 -139
  93. package/dist/core/context.js.map +0 -1
  94. package/dist/core/fields.cjs +0 -2
  95. package/dist/core/fields.cjs.map +0 -1
  96. package/dist/core/fields.d.ts +0 -52
  97. package/dist/core/fields.d.ts.map +0 -1
  98. package/dist/core/fields.js +0 -310
  99. package/dist/core/fields.js.map +0 -1
  100. package/dist/core/lifecycle.cjs +0 -2
  101. package/dist/core/lifecycle.cjs.map +0 -1
  102. package/dist/core/lifecycle.d.ts +0 -15
  103. package/dist/core/lifecycle.d.ts.map +0 -1
  104. package/dist/core/lifecycle.js +0 -38
  105. package/dist/core/lifecycle.js.map +0 -1
  106. package/dist/core/notifier.cjs +0 -2
  107. package/dist/core/notifier.cjs.map +0 -1
  108. package/dist/core/notifier.d.ts +0 -33
  109. package/dist/core/notifier.d.ts.map +0 -1
  110. package/dist/core/notifier.js +0 -78
  111. package/dist/core/notifier.js.map +0 -1
  112. package/dist/core/observe.cjs +0 -2
  113. package/dist/core/observe.cjs.map +0 -1
  114. package/dist/core/observe.d.ts +0 -20
  115. package/dist/core/observe.d.ts.map +0 -1
  116. package/dist/core/observe.js +0 -79
  117. package/dist/core/observe.js.map +0 -1
  118. package/dist/core/scope.cjs +0 -2
  119. package/dist/core/scope.cjs.map +0 -1
  120. package/dist/core/scope.d.ts +0 -11
  121. package/dist/core/scope.d.ts.map +0 -1
  122. package/dist/core/scope.js +0 -235
  123. package/dist/core/scope.js.map +0 -1
  124. package/dist/types.cjs +0 -2
  125. package/dist/types.cjs.map +0 -1
  126. package/dist/types.js +0 -21
  127. package/dist/types.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"fields.js","names":[],"sources":["../../src/core/fields.ts"],"sourcesContent":["import { isAbortError } from '@vielzeug/arsenal';\n\nimport type { FormContext } from './context';\n\nimport { warn } from '../_dev';\nimport { anySignal, assertSafeKey, flattenValues, isSafeKey, sanitizeForLog, unflattenValues } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\nimport {\n type ArrayField,\n type ErrorKeyOf,\n type FieldState,\n type FieldValidator,\n type FlatKeyOf,\n type MaybePromise,\n type RegisterFieldOptions,\n type SetOptions,\n type SubmitResult,\n type TypeAtPath,\n type Unsubscribe,\n type ValidateResult,\n} from '../types';\nimport { createArrayField } from './array';\n\n/**\n * Field values, dirty/touch tracking, field-level errors, dynamic field registration, and\n * validation/submit — one module, not the two (`values.ts`/`validation.ts`) it used to be\n * split across. That split was drawn along \"which section of the original monolithic\n * `createForm()` this code used to live in,\" not along a real boundary: validating a field\n * needs the same store/fieldErrors/fieldCtrls state that setting one does, so the old split\n * required threading `deps: Pick<ValueOps, '...'>` between files just to call each other. One\n * closure removes that threading entirely.\n *\n * The six `*Keys(predicate)` functions below are the shared bulk-mutation primitives behind\n * `reset`/`replace`/`patch`/`touchAll`/`untouchAll`/`resetErrors`. `scope.ts` calls them with\n * an `isScopedKey` predicate instead of maintaining its own second copy of each loop.\n */\nexport function createFieldOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>) {\n /* ======== Values ======== */\n\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 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 function set<K extends FlatKeyOf<TValues>>(name: K, value: TypeAtPath<TValues, K>, options: SetOptions = {}): void {\n ctx.ensureNotDisposed('set');\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 /* ======== Errors ======== */\n\n function setError(name: ErrorKeyOf<TValues>, message: string): void {\n ctx.ensureNotDisposed('setError');\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('clearError');\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 /**\n * Shared primitive behind `resetErrors()` (predicate `() => true`) and `scope.ts`'s scoped\n * `resetErrors()` (predicate `isScopedKey`, mapper `pre`). Clears every error matching\n * `predicate`, then applies `nextErrors` through `toFullKey`.\n */\n function resetErrorsKeys(\n predicate: (key: string) => boolean,\n nextErrors: Partial<Record<string, string | undefined>>,\n toFullKey: (key: string) => string,\n ): void {\n for (const key of [...ctx.fieldErrors.keys()]) {\n if (predicate(key)) ctx.fieldErrors.delete(key);\n }\n\n for (const [key, message] of Object.entries(nextErrors)) {\n const fullKey = toFullKey(key);\n\n assertSafeKey(fullKey);\n\n if (typeof message === 'string') ctx.fieldErrors.set(fullKey, message);\n }\n\n ctx.invalidateErrors();\n }\n\n function resetErrors(nextErrors: Partial<Record<ErrorKeyOf<TValues>, string | undefined>> = {}): void {\n ctx.ensureNotDisposed('resetErrors');\n resetErrorsKeys(\n () => true,\n nextErrors,\n (k) => k,\n );\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('setValidator');\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('fields.register');\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 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('touch');\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('untouch');\n\n const key = name as string;\n\n assertSafeKey(key);\n\n if (ctx.touched.delete(key)) ctx.requestNotify(key);\n }\n\n /** Shared by `touchAll()` and scope.ts's scoped `touchAll()`. */\n function touchAllKeys(predicate: (key: string) => boolean): void {\n for (const name of ctx.store.keys()) if (predicate(name)) ctx.touched.add(name);\n for (const name of ctx.validators.keys()) if (predicate(name)) ctx.touched.add(name);\n }\n\n function touchAll(): void {\n ctx.ensureNotDisposed('touchAll');\n touchAllKeys(() => true);\n ctx.requestNotify();\n }\n\n /** Shared by `untouchAll()` and scope.ts's scoped `untouchAll()`. */\n function untouchAllKeys(predicate: (key: string) => boolean): void {\n for (const name of [...ctx.touched]) if (predicate(name)) ctx.touched.delete(name);\n }\n\n function untouchAll(): void {\n ctx.ensureNotDisposed('untouchAll');\n untouchAllKeys(() => true);\n ctx.requestNotify();\n }\n\n /* ======== Lifecycle: field operations ======== */\n\n function resetField(name: FlatKeyOf<TValues>): void {\n ctx.ensureNotDisposed('resetField');\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('fields.remove');\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 /**\n * Every key a bulk operation might need to touch — not just `store.keys()`. A validator-only\n * field (a validator with no matching store entry, e.g. a cross-field or virtual-name\n * validator) can still be `touched` or hold a `fieldErrors` entry, so `resetKeys`/\n * `replaceKeys` below must consider `touched`/`fieldErrors`/`validators` too, not just\n * `store` — deriving \"everything reset must clear\" from `store.keys()` alone silently leaves\n * such a field's touched/error state behind.\n */\n function allKnownKeys(): Set<string> {\n return new Set([...ctx.store.keys(), ...ctx.touched, ...ctx.fieldErrors.keys(), ...ctx.validators.keys()]);\n }\n\n /**\n * Shared primitive behind `reset()` (predicate `() => true`) and scope.ts's scoped\n * `reset()` (predicate `isScopedKey`). For each known key matching `predicate`: aborts its\n * in-flight field validation, restores its baseline value (or removes it entirely if it has\n * no baseline entry — e.g. a key written via `set()` alone, never through `patch()`), and\n * clears its dirty/touched/error state.\n */\n function resetKeys(predicate: (key: string) => boolean): void {\n for (const key of allKnownKeys()) {\n if (!predicate(key)) continue;\n\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n\n // Both branches are no-ops for a validator-only key that was never in `store` to begin\n // with — `Map.delete()` on an absent key is a safe no-op, and `baseline.has(key)` is\n // false for it, so it falls through to `delete` rather than writing an `undefined` entry.\n if (ctx.baseline.has(key)) ctx.store.set(key, ctx.baseline.get(key));\n else ctx.store.delete(key);\n\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n }\n }\n\n function reset(): void {\n ctx.ensureNotDisposed('reset');\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.validatingRuns.clear();\n resetKeys(() => true);\n ctx.submitCount = 0;\n ctx.requestNotify();\n }\n\n /**\n * Shared primitive behind `replace()` (predicate `() => true`, mapper `(k) => k`) and\n * scope.ts's scoped `replace()` (predicate `isScopedKey`, mapper `pre`). Removes every\n * store/baseline/touched/error entry matching `predicate` (see `allKnownKeys()` above for\n * why this isn't just `store.keys()`), then writes `flat` back in as both store and baseline\n * (a replace always establishes a new, clean baseline).\n */\n function replaceKeys(\n predicate: (key: string) => boolean,\n flat: Record<string, unknown>,\n toFullKey: (key: string) => string,\n ): void {\n for (const key of allKnownKeys()) {\n if (!predicate(key)) continue;\n\n ctx.fieldCtrls.get(key)?.abort();\n ctx.fieldCtrls.delete(key);\n ctx.store.delete(key);\n ctx.baseline.delete(key);\n ctx.dirty.delete(key);\n ctx.touched.delete(key);\n\n if (ctx.fieldErrors.delete(key)) ctx.invalidateErrors();\n }\n\n for (const [key, value] of Object.entries(flat)) {\n const fullKey = toFullKey(key);\n\n ctx.store.set(fullKey, value);\n ctx.baseline.set(fullKey, value);\n }\n }\n\n function replace(newValues: TValues): void {\n ctx.ensureNotDisposed('replace');\n\n for (const ctrl of ctx.runCtrls) ctrl.abort();\n\n ctx.runCtrls.clear();\n ctx.validatingRuns.clear();\n replaceKeys(\n () => true,\n flattenValues(newValues as Record<string, unknown>),\n (k) => k,\n );\n ctx.submitCount = 0;\n ctx.requestNotify();\n }\n\n /**\n * Shared primitive behind `patch()` and scope.ts's scoped `patch()` (mapper `pre`). Unlike\n * `resetKeys`/`replaceKeys`, `patch()` never needs a predicate — the partial object itself\n * already names exactly the keys to touch. Returns the full (prefixed) keys written, for\n * the caller to pass to `requestNotify()`.\n */\n function patchKeys(partial: Record<string, unknown>, toFullKey: (key: string) => string): string[] {\n const flat = flattenValues(partial);\n const changedKeys: string[] = [];\n\n for (const [key, value] of Object.entries(flat)) {\n const fullKey = toFullKey(key);\n\n ctx.baseline.set(fullKey, value);\n ctx.store.set(fullKey, value);\n ctx.dirty.delete(fullKey);\n changedKeys.push(fullKey);\n }\n\n return changedKeys;\n }\n\n function patch(partial: Record<string, unknown>): void {\n ctx.ensureNotDisposed('patch');\n ctx.requestNotify(patchKeys(partial, (k) => k));\n }\n\n /* ======== Array helpers ======== */\n\n function array(name: FlatKeyOf<TValues>): ArrayField {\n ctx.ensureNotDisposed('array');\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 /* ======== Validation ======== */\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(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('validate');\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('validate');\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('submit');\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 touchAll();\n });\n\n try {\n const validation = await runValidationCore([...ctx.validators.keys()], 'full');\n\n ctx.ensureNotDisposed('submit');\n\n if (!validation.valid) {\n return { errors: validation.errors, ok: false, type: 'validation' };\n }\n\n return { ok: true, value: await handler(values()) };\n } finally {\n ctx.isSubmittingState = false;\n\n if (!ctx.disposed) ctx.requestNotify();\n }\n }\n\n return {\n array,\n clearError,\n field,\n get,\n listFields,\n patch,\n patchKeys,\n registerField,\n removeField,\n replace,\n replaceKeys,\n reset,\n resetErrors,\n resetErrorsKeys,\n resetField,\n resetKeys,\n runValidationCore,\n set,\n setError,\n setValidator,\n submit,\n submitOrThrow,\n touch,\n touchAll,\n touchAllKeys,\n untouch,\n untouchAll,\n untouchAllKeys,\n validate,\n validateFields,\n values,\n };\n}\n\nexport type FieldOps<TValues extends Record<string, unknown>> = ReturnType<typeof createFieldOps<TValues>>;\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("../_utils.cjs");function t(t,n){function r(){t.dispose(),n.onDispose?.()}function i(){return t.ensureNotDisposed(`history.snapshot`),Object.freeze({baseline:Object.freeze(Object.fromEntries(t.baseline)),dirty:Object.freeze([...t.dirty]),errors:Object.freeze(Object.fromEntries(t.fieldErrors)),store:Object.freeze(Object.fromEntries(t.store)),submitCount:t.submitCount,touched:Object.freeze([...t.touched])})}function a(n){t.ensureNotDisposed(`history.restore`);for(let e of t.runCtrls)e.abort();for(let e of t.fieldCtrls.values())e.abort();t.runCtrls.clear(),t.fieldCtrls.clear(),t.validatingRuns.clear(),t.store.clear(),t.baseline.clear(),t.fieldErrors.clear(),t.touched.clear(),t.dirty.clear();for(let[r,i]of Object.entries(n.store))e.isSafeKey(r)&&t.store.set(r,i);for(let[r,i]of Object.entries(n.baseline))e.isSafeKey(r)&&t.baseline.set(r,i);for(let[r,i]of Object.entries(n.errors))e.isSafeKey(r)&&t.fieldErrors.set(r,i);for(let r of n.touched)e.isSafeKey(r)&&t.touched.add(r);for(let r of n.dirty)e.isSafeKey(r)&&t.dirty.add(r);t.submitCount=n.submitCount,t.invalidateErrors(),t.invalidateValues(),t.requestNotify()}return{dispose:r,restore:a,snapshot:i}}exports.createLifecycleOps=t;
2
- //# sourceMappingURL=lifecycle.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.cjs","names":[],"sources":["../../src/core/lifecycle.ts"],"sourcesContent":["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>>(\n ctx: FormContext<TValues>,\n deps: { onDispose?: () => void },\n) {\n function dispose(): void {\n ctx.dispose();\n deps.onDispose?.();\n }\n\n function snapshot(): FormSnapshot<TValues> {\n ctx.ensureNotDisposed('history.snapshot');\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('history.restore');\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"],"mappings":"iCASA,SAAgB,EACd,EACA,EACA,CACA,SAAS,GAAgB,CACvB,EAAI,QAAQ,EACZ,EAAK,YAAY,CACnB,CAEA,SAAS,GAAkC,CAMzC,OALA,EAAI,kBAAkB,kBAAkB,EAKjC,OAAO,OAAO,CACnB,SAAU,OAAO,OAAO,OAAO,YAAY,EAAI,QAAQ,CAAC,EACxD,MAAO,OAAO,OAAO,CAAC,GAAG,EAAI,KAAK,CAAC,EACnC,OAAQ,OAAO,OAAO,OAAO,YAAY,EAAI,WAAW,CAAC,EACzD,MAAO,OAAO,OAAO,OAAO,YAAY,EAAI,KAAK,CAAC,EAClD,YAAa,EAAI,YACjB,QAAS,OAAO,OAAO,CAAC,GAAG,EAAI,OAAO,CAAC,CACzC,CAAC,CACH,CAEA,SAAS,EAAQ,EAAmC,CAClD,EAAI,kBAAkB,iBAAiB,EAEvC,IAAK,IAAM,KAAQ,EAAI,SAAU,EAAK,MAAM,EAC5C,IAAK,IAAM,KAAQ,EAAI,WAAW,OAAO,EAAG,EAAK,MAAM,EAEvD,EAAI,SAAS,MAAM,EACnB,EAAI,WAAW,MAAM,EACrB,EAAI,eAAe,MAAM,EACzB,EAAI,MAAM,MAAM,EAChB,EAAI,SAAS,MAAM,EACnB,EAAI,YAAY,MAAM,EACtB,EAAI,QAAQ,MAAM,EAClB,EAAI,MAAM,MAAM,EAKhB,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,KAAK,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,MAAM,IAAI,EAAG,CAAC,EACrF,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,QAAQ,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,SAAS,IAAI,EAAG,CAAC,EAC3F,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAK,MAAM,EAAO,EAAA,UAAU,CAAC,GAAG,EAAI,YAAY,IAAI,EAAG,CAAC,EAC5F,IAAK,IAAM,KAAK,EAAK,QAAa,EAAA,UAAU,CAAC,GAAG,EAAI,QAAQ,IAAI,CAAC,EACjE,IAAK,IAAM,KAAK,EAAK,MAAW,EAAA,UAAU,CAAC,GAAG,EAAI,MAAM,IAAI,CAAC,EAE7D,EAAI,YAAc,EAAK,YAEvB,EAAI,iBAAiB,EACrB,EAAI,iBAAiB,EACrB,EAAI,cAAc,CACpB,CAEA,MAAO,CAAE,UAAS,UAAS,UAAS,CACtC"}
@@ -1,15 +0,0 @@
1
- import type { FormSnapshot } from '../types';
2
- import type { FormContext } from './context';
3
- /**
4
- * Lifecycle operations: `dispose`, `snapshot`, `restore`. Moved verbatim from `createForm()`'s
5
- * "Lifecycle: dispose" and "Snapshot / Restore" sections.
6
- */
7
- export declare function createLifecycleOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>, deps: {
8
- onDispose?: () => void;
9
- }): {
10
- dispose: () => void;
11
- restore: (snap: FormSnapshot<TValues>) => void;
12
- snapshot: () => FormSnapshot<TValues>;
13
- };
14
- export type LifecycleOps<TValues extends Record<string, unknown>> = ReturnType<typeof createLifecycleOps<TValues>>;
15
- //# sourceMappingURL=lifecycle.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/core/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI7C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxE,GAAG,EAAE,WAAW,CAAC,OAAO,CAAC,EACzB,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE;mBAEZ,IAAI;oBAqBD,YAAY,CAAC,OAAO,CAAC,KAAG,IAAI;oBAhB9B,YAAY,CAAC,OAAO,CAAC;EAgD3C;AAED,MAAM,MAAM,YAAY,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC"}
@@ -1,38 +0,0 @@
1
- import { isSafeKey as e } from "../_utils.js";
2
- //#region src/core/lifecycle.ts
3
- function t(t, n) {
4
- function r() {
5
- t.dispose(), n.onDispose?.();
6
- }
7
- function i() {
8
- return t.ensureNotDisposed("history.snapshot"), Object.freeze({
9
- baseline: Object.freeze(Object.fromEntries(t.baseline)),
10
- dirty: Object.freeze([...t.dirty]),
11
- errors: Object.freeze(Object.fromEntries(t.fieldErrors)),
12
- store: Object.freeze(Object.fromEntries(t.store)),
13
- submitCount: t.submitCount,
14
- touched: Object.freeze([...t.touched])
15
- });
16
- }
17
- function a(n) {
18
- t.ensureNotDisposed("history.restore");
19
- for (let e of t.runCtrls) e.abort();
20
- for (let e of t.fieldCtrls.values()) e.abort();
21
- t.runCtrls.clear(), t.fieldCtrls.clear(), t.validatingRuns.clear(), t.store.clear(), t.baseline.clear(), t.fieldErrors.clear(), t.touched.clear(), t.dirty.clear();
22
- for (let [r, i] of Object.entries(n.store)) e(r) && t.store.set(r, i);
23
- for (let [r, i] of Object.entries(n.baseline)) e(r) && t.baseline.set(r, i);
24
- for (let [r, i] of Object.entries(n.errors)) e(r) && t.fieldErrors.set(r, i);
25
- for (let r of n.touched) e(r) && t.touched.add(r);
26
- for (let r of n.dirty) e(r) && t.dirty.add(r);
27
- t.submitCount = n.submitCount, t.invalidateErrors(), t.invalidateValues(), t.requestNotify();
28
- }
29
- return {
30
- dispose: r,
31
- restore: a,
32
- snapshot: i
33
- };
34
- }
35
- //#endregion
36
- export { t as createLifecycleOps };
37
-
38
- //# sourceMappingURL=lifecycle.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.js","names":[],"sources":["../../src/core/lifecycle.ts"],"sourcesContent":["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>>(\n ctx: FormContext<TValues>,\n deps: { onDispose?: () => void },\n) {\n function dispose(): void {\n ctx.dispose();\n deps.onDispose?.();\n }\n\n function snapshot(): FormSnapshot<TValues> {\n ctx.ensureNotDisposed('history.snapshot');\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('history.restore');\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"],"mappings":";;AASA,SAAgB,EACd,GACA,GACA;CACA,SAAS,IAAgB;EAEvB,AADA,EAAI,QAAQ,GACZ,EAAK,YAAY;CACnB;CAEA,SAAS,IAAkC;EAMzC,OALA,EAAI,kBAAkB,kBAAkB,GAKjC,OAAO,OAAO;GACnB,UAAU,OAAO,OAAO,OAAO,YAAY,EAAI,QAAQ,CAAC;GACxD,OAAO,OAAO,OAAO,CAAC,GAAG,EAAI,KAAK,CAAC;GACnC,QAAQ,OAAO,OAAO,OAAO,YAAY,EAAI,WAAW,CAAC;GACzD,OAAO,OAAO,OAAO,OAAO,YAAY,EAAI,KAAK,CAAC;GAClD,aAAa,EAAI;GACjB,SAAS,OAAO,OAAO,CAAC,GAAG,EAAI,OAAO,CAAC;EACzC,CAAC;CACH;CAEA,SAAS,EAAQ,GAAmC;EAClD,EAAI,kBAAkB,iBAAiB;EAEvC,KAAK,IAAM,KAAQ,EAAI,UAAU,EAAK,MAAM;EAC5C,KAAK,IAAM,KAAQ,EAAI,WAAW,OAAO,GAAG,EAAK,MAAM;EASvD,AAPA,EAAI,SAAS,MAAM,GACnB,EAAI,WAAW,MAAM,GACrB,EAAI,eAAe,MAAM,GACzB,EAAI,MAAM,MAAM,GAChB,EAAI,SAAS,MAAM,GACnB,EAAI,YAAY,MAAM,GACtB,EAAI,QAAQ,MAAM,GAClB,EAAI,MAAM,MAAM;EAKhB,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAK,KAAK,GAAG,AAAI,EAAU,CAAC,KAAG,EAAI,MAAM,IAAI,GAAG,CAAC;EACrF,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAK,QAAQ,GAAG,AAAI,EAAU,CAAC,KAAG,EAAI,SAAS,IAAI,GAAG,CAAC;EAC3F,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAK,MAAM,GAAG,AAAI,EAAU,CAAC,KAAG,EAAI,YAAY,IAAI,GAAG,CAAC;EAC5F,KAAK,IAAM,KAAK,EAAK,SAAS,AAAI,EAAU,CAAC,KAAG,EAAI,QAAQ,IAAI,CAAC;EACjE,KAAK,IAAM,KAAK,EAAK,OAAO,AAAI,EAAU,CAAC,KAAG,EAAI,MAAM,IAAI,CAAC;EAM7D,AAJA,EAAI,cAAc,EAAK,aAEvB,EAAI,iBAAiB,GACrB,EAAI,iBAAiB,GACrB,EAAI,cAAc;CACpB;CAEA,OAAO;EAAE;EAAS;EAAS;CAAS;AACtC"}
@@ -1,2 +0,0 @@
1
- const e=require("../_utils.cjs");let t=require("@vielzeug/ripple");function n(n){let r=(0,t.signal)(n.computeState()),i=new Map,a=new Map,o=new Set,s=!1;function c(e){let r=i.get(e);return r||(r=(0,t.signal)(n.buildFieldState(e),{equals:(e,t)=>e.value===t.value&&e.error===t.error&&e.hasError===t.hasError&&e.touched===t.touched&&e.dirty===t.dirty}),i.set(e,r)),r}function l(e){let t=a.get(e);if(t)return t;let r=Object.freeze(n.buildFieldState(e));return a.set(e,r),r}function u(){return r.value}function d(e){if(!s)if(n.invalidateCaches(),e===void 0)a.clear(),(0,t.batch)(()=>{r.value=n.computeState();for(let[e,t]of i)t.value=n.buildFieldState(e)});else if(typeof e==`string`)a.delete(e),(0,t.batch)(()=>{r.value=n.computeState();let t=i.get(e);t&&(t.value=n.buildFieldState(e))});else{let o=[...e];for(let e of o)a.delete(e);(0,t.batch)(()=>{r.value=n.computeState();for(let e of o){let t=i.get(e);t&&(t.value=n.buildFieldState(e))}})}}function f(e,n){if(s)return()=>{};let i=(0,t.watch)(r,t=>{e(t)});return o.add(i),n?.sync&&e(r.value),()=>{i.dispose(),o.delete(i)}}function p(n,r,i){if(s)return()=>{};e.assertSafeKey(n);let a=c(n),l=(0,t.watch)(a,e=>{r(e)});return o.add(l),i?.sync&&r(a.value),()=>{l.dispose(),o.delete(l)}}function m(){s=!0;for(let e of o)e.dispose();o.clear(),i.clear(),a.clear()}return{dispose:m,getFieldSnapshot:l,getOrCreateFieldSignal:c,getStateSnapshot:u,requestNotify:d,subscribe:f,subscribeField:p}}exports.createNotifier=n;
2
- //# sourceMappingURL=notifier.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notifier.cjs","names":[],"sources":["../../src/core/notifier.ts"],"sourcesContent":["import { batch as rippleBatch, signal, watch, type Signal } from '@vielzeug/ripple';\n\nimport type { FieldState, FormState, SubscribeOptions, Unsubscribe } from '../types';\n\nimport { assertSafeKey } from '../_utils';\n\n/**\n * Everything needed to (re)compute fresh state on demand. Supplied by whoever owns the\n * actual field/validator Maps (`core/context.ts`, backing `core/fields.ts`) so this module\n * never touches those Maps directly — it only knows \"how to get current state\" and \"who's\n * listening,\" never \"where the state lives.\"\n */\nexport type NotifierDeps = {\n buildFieldState(name: string): FieldState<unknown>;\n computeState(): FormState;\n /** Called once at the top of every requestNotify(), before state is recomputed — lets the\n * caller drop its own memoized derived values (e.g. `values()`'s cached unflattened object). */\n invalidateCaches(): void;\n};\n\n/**\n * Owns every signal, subscription, and derived-state cache a form needs to notify observers.\n * Extracted from the old shared `FormContext` god-object (see `core/context.ts`'s header\n * comment) — this is the one piece of that object with no real dependency on field values or\n * validators, only on \"what does current state look like\" (via `NotifierDeps`), so it's safe\n * to own its signals/caches privately instead of exposing them on a shared mutable bag.\n */\nexport function createNotifier(deps: NotifierDeps) {\n const formStateSignal: Signal<FormState> = signal(deps.computeState());\n const fieldSignals = new Map<string, Signal<FieldState<unknown>>>();\n const fieldStateCache = new Map<string, FieldState<unknown>>();\n const rippleSubs = new Set<{ dispose(): void }>();\n let disposed = false;\n\n function getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>> {\n let sig = fieldSignals.get(key);\n\n if (!sig) {\n sig = signal<FieldState<unknown>>(deps.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 fieldSignals.set(key, sig);\n }\n\n return sig;\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(deps.buildFieldState(name));\n\n fieldStateCache.set(name, snap);\n\n return snap;\n }\n\n function getStateSnapshot(): FormState {\n return formStateSignal.value;\n }\n\n /**\n * Unified notification — a single write path for every mutation in the package.\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 deps.invalidateCaches();\n\n if (target === undefined) {\n fieldStateCache.clear();\n rippleBatch(() => {\n formStateSignal.value = deps.computeState();\n\n for (const [name, sig] of fieldSignals) sig.value = deps.buildFieldState(name);\n });\n } else if (typeof target === 'string') {\n fieldStateCache.delete(target);\n rippleBatch(() => {\n formStateSignal.value = deps.computeState();\n\n const sig = fieldSignals.get(target);\n\n if (sig) sig.value = deps.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 = deps.computeState();\n\n for (const field of fields) {\n const sig = fieldSignals.get(field);\n\n if (sig) sig.value = deps.buildFieldState(field);\n }\n });\n }\n }\n\n function subscribe(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe {\n if (disposed) return () => {};\n\n // Wrap listener so it always returns void — ripple watch throws on non-function returns.\n const sub = watch(formStateSignal, (state) => {\n listener(state);\n });\n\n rippleSubs.add(sub);\n\n if (options?.sync) listener(formStateSignal.value);\n\n return () => {\n sub.dispose();\n rippleSubs.delete(sub);\n };\n }\n\n function subscribeField(\n name: string,\n listener: (state: FieldState<unknown>) => void,\n options?: SubscribeOptions,\n ): Unsubscribe {\n if (disposed) return () => {};\n\n assertSafeKey(name);\n\n const sig = getOrCreateFieldSignal(name);\n const sub = watch(sig, (state) => {\n listener(state);\n });\n\n rippleSubs.add(sub);\n\n if (options?.sync) listener(sig.value);\n\n return () => {\n sub.dispose();\n rippleSubs.delete(sub);\n };\n }\n\n function dispose(): void {\n disposed = true;\n\n for (const sub of rippleSubs) sub.dispose();\n\n rippleSubs.clear();\n fieldSignals.clear();\n fieldStateCache.clear();\n }\n\n return {\n dispose,\n getFieldSnapshot,\n getOrCreateFieldSignal,\n getStateSnapshot,\n requestNotify,\n subscribe,\n subscribeField,\n };\n}\n\nexport type Notifier = ReturnType<typeof createNotifier>;\n"],"mappings":"mEA2BA,SAAgB,EAAe,EAAoB,CACjD,IAAM,GAAA,EAAA,EAAA,OAAA,CAA4C,EAAK,aAAa,CAAC,EAC/D,EAAe,IAAI,IACnB,EAAkB,IAAI,IACtB,EAAa,IAAI,IACnB,EAAW,GAEf,SAAS,EAAuB,EAA0C,CACxE,IAAI,EAAM,EAAa,IAAI,CAAG,EAc9B,OAZK,IACH,GAAA,EAAA,EAAA,OAAA,CAAkC,EAAK,gBAAgB,CAAG,EAAG,CAC3D,QAAS,EAAG,IACV,EAAE,QAAU,EAAE,OACd,EAAE,QAAU,EAAE,OACd,EAAE,WAAa,EAAE,UACjB,EAAE,UAAY,EAAE,SAChB,EAAE,QAAU,EAAE,KAClB,CAAC,EACD,EAAa,IAAI,EAAK,CAAG,GAGpB,CACT,CAEA,SAAS,EAAiB,EAAmC,CAC3D,IAAM,EAAS,EAAgB,IAAI,CAAI,EAEvC,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAO,OAAO,OAAO,EAAK,gBAAgB,CAAI,CAAC,EAIrD,OAFA,EAAgB,IAAI,EAAM,CAAI,EAEvB,CACT,CAEA,SAAS,GAA8B,CACrC,OAAO,EAAgB,KACzB,CASA,SAAS,EAAc,EAA0C,CAC3D,MAIJ,GAFA,EAAK,iBAAiB,EAElB,IAAW,IAAA,GACb,EAAgB,MAAM,GACtB,EAAA,EAAA,MAAA,KAAkB,CAChB,EAAgB,MAAQ,EAAK,aAAa,EAE1C,IAAK,GAAM,CAAC,EAAM,KAAQ,EAAc,EAAI,MAAQ,EAAK,gBAAgB,CAAI,CAC/E,CAAC,OACI,GAAI,OAAO,GAAW,SAC3B,EAAgB,OAAO,CAAM,GAC7B,EAAA,EAAA,MAAA,KAAkB,CAChB,EAAgB,MAAQ,EAAK,aAAa,EAE1C,IAAM,EAAM,EAAa,IAAI,CAAM,EAE/B,IAAK,EAAI,MAAQ,EAAK,gBAAgB,CAAM,EAClD,CAAC,MACI,CAEL,IAAM,EAAS,CAAC,GAAG,CAAM,EAEzB,IAAK,IAAM,KAAS,EAAQ,EAAgB,OAAO,CAAK,GAExD,EAAA,EAAA,MAAA,KAAkB,CAChB,EAAgB,MAAQ,EAAK,aAAa,EAE1C,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAM,EAAa,IAAI,CAAK,EAE9B,IAAK,EAAI,MAAQ,EAAK,gBAAgB,CAAK,EACjD,CACF,CAAC,CACH,CACF,CAEA,SAAS,EAAU,EAAsC,EAAyC,CAChG,GAAI,EAAU,UAAa,CAAC,EAG5B,IAAM,GAAA,EAAA,EAAA,MAAA,CAAY,EAAkB,GAAU,CAC5C,EAAS,CAAK,CAChB,CAAC,EAMD,OAJA,EAAW,IAAI,CAAG,EAEd,GAAS,MAAM,EAAS,EAAgB,KAAK,MAEpC,CACX,EAAI,QAAQ,EACZ,EAAW,OAAO,CAAG,CACvB,CACF,CAEA,SAAS,EACP,EACA,EACA,EACa,CACb,GAAI,EAAU,UAAa,CAAC,EAE5B,EAAA,cAAc,CAAI,EAElB,IAAM,EAAM,EAAuB,CAAI,EACjC,GAAA,EAAA,EAAA,MAAA,CAAY,EAAM,GAAU,CAChC,EAAS,CAAK,CAChB,CAAC,EAMD,OAJA,EAAW,IAAI,CAAG,EAEd,GAAS,MAAM,EAAS,EAAI,KAAK,MAExB,CACX,EAAI,QAAQ,EACZ,EAAW,OAAO,CAAG,CACvB,CACF,CAEA,SAAS,GAAgB,CACvB,EAAW,GAEX,IAAK,IAAM,KAAO,EAAY,EAAI,QAAQ,EAE1C,EAAW,MAAM,EACjB,EAAa,MAAM,EACnB,EAAgB,MAAM,CACxB,CAEA,MAAO,CACL,UACA,mBACA,yBACA,mBACA,gBACA,YACA,gBACF,CACF"}
@@ -1,33 +0,0 @@
1
- import { type Signal } from '@vielzeug/ripple';
2
- import type { FieldState, FormState, SubscribeOptions, Unsubscribe } from '../types';
3
- /**
4
- * Everything needed to (re)compute fresh state on demand. Supplied by whoever owns the
5
- * actual field/validator Maps (`core/context.ts`, backing `core/fields.ts`) so this module
6
- * never touches those Maps directly — it only knows "how to get current state" and "who's
7
- * listening," never "where the state lives."
8
- */
9
- export type NotifierDeps = {
10
- buildFieldState(name: string): FieldState<unknown>;
11
- computeState(): FormState;
12
- /** Called once at the top of every requestNotify(), before state is recomputed — lets the
13
- * caller drop its own memoized derived values (e.g. `values()`'s cached unflattened object). */
14
- invalidateCaches(): void;
15
- };
16
- /**
17
- * Owns every signal, subscription, and derived-state cache a form needs to notify observers.
18
- * Extracted from the old shared `FormContext` god-object (see `core/context.ts`'s header
19
- * comment) — this is the one piece of that object with no real dependency on field values or
20
- * validators, only on "what does current state look like" (via `NotifierDeps`), so it's safe
21
- * to own its signals/caches privately instead of exposing them on a shared mutable bag.
22
- */
23
- export declare function createNotifier(deps: NotifierDeps): {
24
- dispose: () => void;
25
- getFieldSnapshot: (name: string) => FieldState<unknown>;
26
- getOrCreateFieldSignal: (key: string) => Signal<FieldState<unknown>>;
27
- getStateSnapshot: () => FormState;
28
- requestNotify: (target?: string | Iterable<string>) => void;
29
- subscribe: (listener: (state: FormState) => void, options?: SubscribeOptions) => Unsubscribe;
30
- subscribeField: (name: string, listener: (state: FieldState<unknown>) => void, options?: SubscribeOptions) => Unsubscribe;
31
- };
32
- export type Notifier = ReturnType<typeof createNotifier>;
33
- //# sourceMappingURL=notifier.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../src/core/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEpF,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAIrF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,IAAI,SAAS,CAAC;IAC1B;oGACgG;IAChG,gBAAgB,IAAI,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY;mBAiI3B,IAAI;6BAxGQ,MAAM,KAAG,UAAU,CAAC,OAAO,CAAC;kCAlBvB,MAAM,KAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;4BA8B5C,SAAS;6BAWN,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAG,IAAI;0BAuCnC,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,YAAY,gBAAgB,KAAG,WAAW;2BAmBzF,MAAM,YACF,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,IAAI,YACpC,gBAAgB,KACzB,WAAW;EAuCf;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -1,78 +0,0 @@
1
- import { assertSafeKey as e } from "../_utils.js";
2
- import { batch as t, signal as n, watch as r } from "@vielzeug/ripple";
3
- //#region src/core/notifier.ts
4
- function i(i) {
5
- let a = n(i.computeState()), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set(), l = !1;
6
- function u(e) {
7
- let t = o.get(e);
8
- return t || (t = n(i.buildFieldState(e), { equals: (e, t) => e.value === t.value && e.error === t.error && e.hasError === t.hasError && e.touched === t.touched && e.dirty === t.dirty }), o.set(e, t)), t;
9
- }
10
- function d(e) {
11
- let t = s.get(e);
12
- if (t) return t;
13
- let n = Object.freeze(i.buildFieldState(e));
14
- return s.set(e, n), n;
15
- }
16
- function f() {
17
- return a.value;
18
- }
19
- function p(e) {
20
- if (!l) if (i.invalidateCaches(), e === void 0) s.clear(), t(() => {
21
- a.value = i.computeState();
22
- for (let [e, t] of o) t.value = i.buildFieldState(e);
23
- });
24
- else if (typeof e == "string") s.delete(e), t(() => {
25
- a.value = i.computeState();
26
- let t = o.get(e);
27
- t && (t.value = i.buildFieldState(e));
28
- });
29
- else {
30
- let n = [...e];
31
- for (let e of n) s.delete(e);
32
- t(() => {
33
- a.value = i.computeState();
34
- for (let e of n) {
35
- let t = o.get(e);
36
- t && (t.value = i.buildFieldState(e));
37
- }
38
- });
39
- }
40
- }
41
- function m(e, t) {
42
- if (l) return () => {};
43
- let n = r(a, (t) => {
44
- e(t);
45
- });
46
- return c.add(n), t?.sync && e(a.value), () => {
47
- n.dispose(), c.delete(n);
48
- };
49
- }
50
- function h(t, n, i) {
51
- if (l) return () => {};
52
- e(t);
53
- let a = u(t), o = r(a, (e) => {
54
- n(e);
55
- });
56
- return c.add(o), i?.sync && n(a.value), () => {
57
- o.dispose(), c.delete(o);
58
- };
59
- }
60
- function g() {
61
- l = !0;
62
- for (let e of c) e.dispose();
63
- c.clear(), o.clear(), s.clear();
64
- }
65
- return {
66
- dispose: g,
67
- getFieldSnapshot: d,
68
- getOrCreateFieldSignal: u,
69
- getStateSnapshot: f,
70
- requestNotify: p,
71
- subscribe: m,
72
- subscribeField: h
73
- };
74
- }
75
- //#endregion
76
- export { i as createNotifier };
77
-
78
- //# sourceMappingURL=notifier.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notifier.js","names":[],"sources":["../../src/core/notifier.ts"],"sourcesContent":["import { batch as rippleBatch, signal, watch, type Signal } from '@vielzeug/ripple';\n\nimport type { FieldState, FormState, SubscribeOptions, Unsubscribe } from '../types';\n\nimport { assertSafeKey } from '../_utils';\n\n/**\n * Everything needed to (re)compute fresh state on demand. Supplied by whoever owns the\n * actual field/validator Maps (`core/context.ts`, backing `core/fields.ts`) so this module\n * never touches those Maps directly — it only knows \"how to get current state\" and \"who's\n * listening,\" never \"where the state lives.\"\n */\nexport type NotifierDeps = {\n buildFieldState(name: string): FieldState<unknown>;\n computeState(): FormState;\n /** Called once at the top of every requestNotify(), before state is recomputed — lets the\n * caller drop its own memoized derived values (e.g. `values()`'s cached unflattened object). */\n invalidateCaches(): void;\n};\n\n/**\n * Owns every signal, subscription, and derived-state cache a form needs to notify observers.\n * Extracted from the old shared `FormContext` god-object (see `core/context.ts`'s header\n * comment) — this is the one piece of that object with no real dependency on field values or\n * validators, only on \"what does current state look like\" (via `NotifierDeps`), so it's safe\n * to own its signals/caches privately instead of exposing them on a shared mutable bag.\n */\nexport function createNotifier(deps: NotifierDeps) {\n const formStateSignal: Signal<FormState> = signal(deps.computeState());\n const fieldSignals = new Map<string, Signal<FieldState<unknown>>>();\n const fieldStateCache = new Map<string, FieldState<unknown>>();\n const rippleSubs = new Set<{ dispose(): void }>();\n let disposed = false;\n\n function getOrCreateFieldSignal(key: string): Signal<FieldState<unknown>> {\n let sig = fieldSignals.get(key);\n\n if (!sig) {\n sig = signal<FieldState<unknown>>(deps.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 fieldSignals.set(key, sig);\n }\n\n return sig;\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(deps.buildFieldState(name));\n\n fieldStateCache.set(name, snap);\n\n return snap;\n }\n\n function getStateSnapshot(): FormState {\n return formStateSignal.value;\n }\n\n /**\n * Unified notification — a single write path for every mutation in the package.\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 deps.invalidateCaches();\n\n if (target === undefined) {\n fieldStateCache.clear();\n rippleBatch(() => {\n formStateSignal.value = deps.computeState();\n\n for (const [name, sig] of fieldSignals) sig.value = deps.buildFieldState(name);\n });\n } else if (typeof target === 'string') {\n fieldStateCache.delete(target);\n rippleBatch(() => {\n formStateSignal.value = deps.computeState();\n\n const sig = fieldSignals.get(target);\n\n if (sig) sig.value = deps.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 = deps.computeState();\n\n for (const field of fields) {\n const sig = fieldSignals.get(field);\n\n if (sig) sig.value = deps.buildFieldState(field);\n }\n });\n }\n }\n\n function subscribe(listener: (state: FormState) => void, options?: SubscribeOptions): Unsubscribe {\n if (disposed) return () => {};\n\n // Wrap listener so it always returns void — ripple watch throws on non-function returns.\n const sub = watch(formStateSignal, (state) => {\n listener(state);\n });\n\n rippleSubs.add(sub);\n\n if (options?.sync) listener(formStateSignal.value);\n\n return () => {\n sub.dispose();\n rippleSubs.delete(sub);\n };\n }\n\n function subscribeField(\n name: string,\n listener: (state: FieldState<unknown>) => void,\n options?: SubscribeOptions,\n ): Unsubscribe {\n if (disposed) return () => {};\n\n assertSafeKey(name);\n\n const sig = getOrCreateFieldSignal(name);\n const sub = watch(sig, (state) => {\n listener(state);\n });\n\n rippleSubs.add(sub);\n\n if (options?.sync) listener(sig.value);\n\n return () => {\n sub.dispose();\n rippleSubs.delete(sub);\n };\n }\n\n function dispose(): void {\n disposed = true;\n\n for (const sub of rippleSubs) sub.dispose();\n\n rippleSubs.clear();\n fieldSignals.clear();\n fieldStateCache.clear();\n }\n\n return {\n dispose,\n getFieldSnapshot,\n getOrCreateFieldSignal,\n getStateSnapshot,\n requestNotify,\n subscribe,\n subscribeField,\n };\n}\n\nexport type Notifier = ReturnType<typeof createNotifier>;\n"],"mappings":";;;AA2BA,SAAgB,EAAe,GAAoB;CACjD,IAAM,IAAqC,EAAO,EAAK,aAAa,CAAC,GAC/D,oBAAe,IAAI,IAAyC,GAC5D,oBAAkB,IAAI,IAAiC,GACvD,oBAAa,IAAI,IAAyB,GAC5C,IAAW;CAEf,SAAS,EAAuB,GAA0C;EACxE,IAAI,IAAM,EAAa,IAAI,CAAG;EAc9B,OAZK,MACH,IAAM,EAA4B,EAAK,gBAAgB,CAAG,GAAG,EAC3D,SAAS,GAAG,MACV,EAAE,UAAU,EAAE,SACd,EAAE,UAAU,EAAE,SACd,EAAE,aAAa,EAAE,YACjB,EAAE,YAAY,EAAE,WAChB,EAAE,UAAU,EAAE,MAClB,CAAC,GACD,EAAa,IAAI,GAAK,CAAG,IAGpB;CACT;CAEA,SAAS,EAAiB,GAAmC;EAC3D,IAAM,IAAS,EAAgB,IAAI,CAAI;EAEvC,IAAI,GAAQ,OAAO;EAEnB,IAAM,IAAO,OAAO,OAAO,EAAK,gBAAgB,CAAI,CAAC;EAIrD,OAFA,EAAgB,IAAI,GAAM,CAAI,GAEvB;CACT;CAEA,SAAS,IAA8B;EACrC,OAAO,EAAgB;CACzB;CASA,SAAS,EAAc,GAA0C;EAC3D,QAIJ,IAFA,EAAK,iBAAiB,GAElB,MAAW,KAAA,GAEb,AADA,EAAgB,MAAM,GACtB,QAAkB;GAChB,EAAgB,QAAQ,EAAK,aAAa;GAE1C,KAAK,IAAM,CAAC,GAAM,MAAQ,GAAc,EAAI,QAAQ,EAAK,gBAAgB,CAAI;EAC/E,CAAC;OACI,IAAI,OAAO,KAAW,UAE3B,AADA,EAAgB,OAAO,CAAM,GAC7B,QAAkB;GAChB,EAAgB,QAAQ,EAAK,aAAa;GAE1C,IAAM,IAAM,EAAa,IAAI,CAAM;GAEnC,AAAI,MAAK,EAAI,QAAQ,EAAK,gBAAgB,CAAM;EAClD,CAAC;OACI;GAEL,IAAM,IAAS,CAAC,GAAG,CAAM;GAEzB,KAAK,IAAM,KAAS,GAAQ,EAAgB,OAAO,CAAK;GAExD,QAAkB;IAChB,EAAgB,QAAQ,EAAK,aAAa;IAE1C,KAAK,IAAM,KAAS,GAAQ;KAC1B,IAAM,IAAM,EAAa,IAAI,CAAK;KAElC,AAAI,MAAK,EAAI,QAAQ,EAAK,gBAAgB,CAAK;IACjD;GACF,CAAC;EACH;CACF;CAEA,SAAS,EAAU,GAAsC,GAAyC;EAChG,IAAI,GAAU,aAAa,CAAC;EAG5B,IAAM,IAAM,EAAM,IAAkB,MAAU;GAC5C,EAAS,CAAK;EAChB,CAAC;EAMD,OAJA,EAAW,IAAI,CAAG,GAEd,GAAS,QAAM,EAAS,EAAgB,KAAK,SAEpC;GAEX,AADA,EAAI,QAAQ,GACZ,EAAW,OAAO,CAAG;EACvB;CACF;CAEA,SAAS,EACP,GACA,GACA,GACa;EACb,IAAI,GAAU,aAAa,CAAC;EAE5B,EAAc,CAAI;EAElB,IAAM,IAAM,EAAuB,CAAI,GACjC,IAAM,EAAM,IAAM,MAAU;GAChC,EAAS,CAAK;EAChB,CAAC;EAMD,OAJA,EAAW,IAAI,CAAG,GAEd,GAAS,QAAM,EAAS,EAAI,KAAK,SAExB;GAEX,AADA,EAAI,QAAQ,GACZ,EAAW,OAAO,CAAG;EACvB;CACF;CAEA,SAAS,IAAgB;EACvB,IAAW;EAEX,KAAK,IAAM,KAAO,GAAY,EAAI,QAAQ;EAI1C,AAFA,EAAW,MAAM,GACjB,EAAa,MAAM,GACnB,EAAgB,MAAM;CACxB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -1,2 +0,0 @@
1
- const e=require("../_utils.cjs"),t=require("./asyncQueue.cjs");let n=require("@vielzeug/arsenal");function r(r,i){function a(e,t){return r.subscribe(e,t)}function o(t,n,i){let a=t;return e.assertSafeKey(a),r.subscribeField(a,n,i)}function s(t,a){r.ensureNotDisposed(`connect`);let o=t;e.assertSafeKey(o);function s(e,t){return a?.[e]??i.connectDefaults[e]??t}let c=s(`touchOnBlur`,!1),l=s(`validateOnBlur`,!1),u=s(`validateOnChange`,!1),d=s(`validateOnTouch`,!1),f=s(`debounce`,0),p=null;function m(){f>0?(p!==null&&clearTimeout(p),p=setTimeout(()=>{p=null,!r.disposed&&i.validateFields([t]).catch(e=>{if(!(0,n.isAbortError)(e))throw e})},f)):i.validateFields([t]).catch(e=>{if(!(0,n.isAbortError)(e))throw e})}let h=!1;return{get dirty(){return r.dirty.has(o)},dispose(){h=!0,p!==null&&(clearTimeout(p),p=null)},get disposed(){return h},get error(){return r.fieldErrors.get(o)},onBlur:()=>{r.disposed||(c&&i.touch(t),l&&m())},onChange:e=>{r.disposed||(i.set(t,e),(u||d&&r.touched.has(o))&&m())},[Symbol.dispose](){this.dispose()},get touched(){return r.touched.has(o)},get value(){return r.store.get(o)}}}function c(){let e=t.createAsyncQueue(()=>n());e.push(r.getStateSnapshot());let n=a(t=>{e.push(t)});return r.disposeController.signal.addEventListener(`abort`,()=>e.terminate(),{once:!0}),e.iterator}return{connect:s,subscribe:a,subscribeField:o,[Symbol.asyncIterator]:c}}exports.createObserveOps=r;
2
- //# sourceMappingURL=observe.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"observe.cjs","names":[],"sources":["../../src/core/observe.ts"],"sourcesContent":["import { isAbortError } from '@vielzeug/arsenal';\n\nimport type { FormContext } from './context';\nimport type { FieldOps } from './fields';\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<\n FieldOps<TValues>,\n 'set' | 'touch' | 'validateFields'\n> & {\n /** Default `connect()` options — form-level, not part of the shared `FormContext` bag. */\n connectDefaults: ConnectOptions;\n};\n\n/**\n * Observation operations: `subscribe`/`subscribeField` (thin pass-throughs to the notifier\n * owned by `FormContext`), `connect` (per-binding debounce timer), and the\n * `[Symbol.asyncIterator]` implementation.\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 return ctx.subscribe(listener, options);\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 const key = name as string;\n\n assertSafeKey(key);\n\n return ctx.subscribeField(key, listener as (state: FieldState<unknown>) => void, options);\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('connect');\n\n const key = name as string;\n\n assertSafeKey(key);\n\n // Three-way fallback (per-call config -> form-level default -> hard default), one place\n // instead of a copy-pasted `config?.x ?? deps.connectDefaults.x ?? fallback` per option.\n function resolve<Opt extends keyof ConnectOptions>(option: Opt, fallback: NonNullable<ConnectOptions[Opt]>) {\n return config?.[option] ?? deps.connectDefaults[option] ?? fallback;\n }\n\n const touchOnBlur = resolve('touchOnBlur', false);\n const validateOnBlur = resolve('validateOnBlur', false);\n const validateOnChange = resolve('validateOnChange', false);\n const validateOnTouch = resolve('validateOnTouch', false);\n const debounceMs = resolve('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"],"mappings":"kGA+BA,SAAgB,EACd,EACA,EACA,CACA,SAAS,EAAU,EAAsC,EAAyC,CAChG,OAAO,EAAI,UAAU,EAAU,CAAO,CACxC,CAEA,SAAS,EACP,EACA,EACA,EACa,CACb,IAAM,EAAM,EAIZ,OAFA,EAAA,cAAc,CAAG,EAEV,EAAI,eAAe,EAAK,EAAkD,CAAO,CAC1F,CAIA,SAAS,EACP,EACA,EAC0C,CAC1C,EAAI,kBAAkB,SAAS,EAE/B,IAAM,EAAM,EAEZ,EAAA,cAAc,CAAG,EAIjB,SAAS,EAA0C,EAAa,EAA4C,CAC1G,OAAO,IAAS,IAAW,EAAK,gBAAgB,IAAW,CAC7D,CAEA,IAAM,EAAc,EAAQ,cAAe,EAAK,EAC1C,EAAiB,EAAQ,iBAAkB,EAAK,EAChD,EAAmB,EAAQ,mBAAoB,EAAK,EACpD,EAAkB,EAAQ,kBAAmB,EAAK,EAClD,EAAa,EAAQ,WAAY,CAAC,EAGpC,EAAsD,KAE1D,SAAS,GAA2B,CAC9B,EAAa,GACX,IAAkB,MAAM,aAAa,CAAa,EAEtD,EAAgB,eAAiB,CAC/B,EAAgB,KAEZ,GAAI,UAER,EAAU,eAAe,CAAC,CAAI,CAAC,CAAC,CAAC,MAAO,GAAiB,CACvD,GAAI,EAAA,EAAA,EAAA,aAAA,CAAc,CAAG,EAAG,MAAM,CAChC,CAAC,CACH,EAAG,CAAU,GAEb,EAAU,eAAe,CAAC,CAAI,CAAC,CAAC,CAAC,MAAO,GAAiB,CACvD,GAAI,EAAA,EAAA,EAAA,aAAA,CAAc,CAAG,EAAG,MAAM,CAChC,CAAC,CAEL,CAEA,IAAI,EAAkB,GAEtB,MAAO,CACL,IAAI,OAAQ,CACV,OAAO,EAAI,MAAM,IAAI,CAAG,CAC1B,EAEA,SAAU,CACR,EAAkB,GAEd,IAAkB,OACpB,aAAa,CAAa,EAC1B,EAAgB,KAEpB,EACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,IAAI,OAAQ,CACV,OAAO,EAAI,YAAY,IAAI,CAAG,CAChC,EACA,WAAc,CACR,EAAI,WAEJ,GAAa,EAAK,MAAM,CAAI,EAE5B,GAAgB,EAAmB,EACzC,EACA,SAAW,GAAkC,CACvC,EAAI,WAER,EAAK,IAAI,EAAM,CAAK,GAEG,GAAqB,GAAmB,EAAI,QAAQ,IAAI,CAAG,IAE9D,EAAmB,EACzC,EACA,CAAC,OAAO,UAAW,CACjB,KAAK,QAAQ,CACf,EACA,IAAI,SAAU,CACZ,OAAO,EAAI,QAAQ,IAAI,CAAG,CAC5B,EACA,IAAI,OAAQ,CACV,OAAO,EAAI,MAAM,IAAI,CAAG,CAC1B,CACF,CACF,CAIA,SAAS,GAAwD,CAC/D,IAAM,EAAQ,EAAA,qBAAkC,EAAY,CAAC,EAE7D,EAAM,KAAK,EAAI,iBAAiB,CAAC,EAEjC,IAAM,EAAc,EAAW,GAAU,CACvC,EAAM,KAAK,CAAK,CAClB,CAAC,EAID,OAFA,EAAI,kBAAkB,OAAO,iBAAiB,YAAe,EAAM,UAAU,EAAG,CAAE,KAAM,EAAK,CAAC,EAEvF,EAAM,QACf,CAEA,MAAO,CAAE,UAAS,YAAW,kBAAiB,OAAO,eAAgB,CAAoB,CAC3F"}
@@ -1,20 +0,0 @@
1
- import type { FormContext } from './context';
2
- import type { FieldOps } from './fields';
3
- import { type ConnectionResult, type ConnectOptions, type FieldState, type FlatKeyOf, type FormState, type SubscribeOptions, type TypeAtPath, type Unsubscribe } from '../types';
4
- type ObserveDeps<TValues extends Record<string, unknown>> = Pick<FieldOps<TValues>, 'set' | 'touch' | 'validateFields'> & {
5
- /** Default `connect()` options — form-level, not part of the shared `FormContext` bag. */
6
- connectDefaults: ConnectOptions;
7
- };
8
- /**
9
- * Observation operations: `subscribe`/`subscribeField` (thin pass-throughs to the notifier
10
- * owned by `FormContext`), `connect` (per-binding debounce timer), and the
11
- * `[Symbol.asyncIterator]` implementation.
12
- */
13
- export declare function createObserveOps<TValues extends Record<string, unknown>>(ctx: FormContext<TValues>, deps: ObserveDeps<TValues>): {
14
- connect: <K extends FlatKeyOf<TValues>>(name: K, config?: ConnectOptions) => ConnectionResult<TypeAtPath<TValues, K>>;
15
- subscribe: (listener: (state: FormState) => void, options?: SubscribeOptions) => Unsubscribe;
16
- subscribeField: <K extends FlatKeyOf<TValues>>(name: K, listener: (state: FieldState<TypeAtPath<TValues, K>>) => void, options?: SubscribeOptions) => Unsubscribe;
17
- [Symbol.asyncIterator]: () => AsyncIterableIterator<FormState>;
18
- };
19
- export {};
20
- //# sourceMappingURL=observe.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/core/observe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,UAAU,CAAC;AAGlB,KAAK,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,IAAI,CAC9D,QAAQ,CAAC,OAAO,CAAC,EACjB,KAAK,GAAG,OAAO,GAAG,gBAAgB,CACnC,GAAG;IACF,0FAA0F;IAC1F,eAAe,EAAE,cAAc,CAAC;CACjC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtE,GAAG,EAAE,WAAW,CAAC,OAAO,CAAC,EACzB,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC;cAoBT,CAAC,SAAS,SAAS,CAAC,OAAO,CAAC,QACrC,CAAC,WACE,cAAc,KACtB,gBAAgB,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;0BArBd,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,YAAY,gBAAgB,KAAG,WAAW;qBAIzE,CAAC,SAAS,SAAS,CAAC,OAAO,CAAC,QAC5C,CAAC,YACG,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,YACnD,gBAAgB,KACzB,WAAW;kCA0GkB,qBAAqB,CAAC,SAAS,CAAC;EAejE"}
@@ -1,79 +0,0 @@
1
- import { assertSafeKey as e } from "../_utils.js";
2
- import { createAsyncQueue as t } from "./asyncQueue.js";
3
- import { isAbortError as n } from "@vielzeug/arsenal";
4
- //#region src/core/observe.ts
5
- function r(r, i) {
6
- function a(e, t) {
7
- return r.subscribe(e, t);
8
- }
9
- function o(t, n, i) {
10
- let a = t;
11
- return e(a), r.subscribeField(a, n, i);
12
- }
13
- function s(t, a) {
14
- r.ensureNotDisposed("connect");
15
- let o = t;
16
- e(o);
17
- function s(e, t) {
18
- return a?.[e] ?? i.connectDefaults[e] ?? t;
19
- }
20
- let c = s("touchOnBlur", !1), l = s("validateOnBlur", !1), u = s("validateOnChange", !1), d = s("validateOnTouch", !1), f = s("debounce", 0), p = null;
21
- function m() {
22
- f > 0 ? (p !== null && clearTimeout(p), p = setTimeout(() => {
23
- p = null, !r.disposed && i.validateFields([t]).catch((e) => {
24
- if (!n(e)) throw e;
25
- });
26
- }, f)) : i.validateFields([t]).catch((e) => {
27
- if (!n(e)) throw e;
28
- });
29
- }
30
- let h = !1;
31
- return {
32
- get dirty() {
33
- return r.dirty.has(o);
34
- },
35
- dispose() {
36
- h = !0, p !== null && (clearTimeout(p), p = null);
37
- },
38
- get disposed() {
39
- return h;
40
- },
41
- get error() {
42
- return r.fieldErrors.get(o);
43
- },
44
- onBlur: () => {
45
- r.disposed || (c && i.touch(t), l && m());
46
- },
47
- onChange: (e) => {
48
- r.disposed || (i.set(t, e), (u || d && r.touched.has(o)) && m());
49
- },
50
- [Symbol.dispose]() {
51
- this.dispose();
52
- },
53
- get touched() {
54
- return r.touched.has(o);
55
- },
56
- get value() {
57
- return r.store.get(o);
58
- }
59
- };
60
- }
61
- function c() {
62
- let e = t(() => n());
63
- e.push(r.getStateSnapshot());
64
- let n = a((t) => {
65
- e.push(t);
66
- });
67
- return r.disposeController.signal.addEventListener("abort", () => e.terminate(), { once: !0 }), e.iterator;
68
- }
69
- return {
70
- connect: s,
71
- subscribe: a,
72
- subscribeField: o,
73
- [Symbol.asyncIterator]: c
74
- };
75
- }
76
- //#endregion
77
- export { r as createObserveOps };
78
-
79
- //# sourceMappingURL=observe.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"observe.js","names":[],"sources":["../../src/core/observe.ts"],"sourcesContent":["import { isAbortError } from '@vielzeug/arsenal';\n\nimport type { FormContext } from './context';\nimport type { FieldOps } from './fields';\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<\n FieldOps<TValues>,\n 'set' | 'touch' | 'validateFields'\n> & {\n /** Default `connect()` options — form-level, not part of the shared `FormContext` bag. */\n connectDefaults: ConnectOptions;\n};\n\n/**\n * Observation operations: `subscribe`/`subscribeField` (thin pass-throughs to the notifier\n * owned by `FormContext`), `connect` (per-binding debounce timer), and the\n * `[Symbol.asyncIterator]` implementation.\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 return ctx.subscribe(listener, options);\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 const key = name as string;\n\n assertSafeKey(key);\n\n return ctx.subscribeField(key, listener as (state: FieldState<unknown>) => void, options);\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('connect');\n\n const key = name as string;\n\n assertSafeKey(key);\n\n // Three-way fallback (per-call config -> form-level default -> hard default), one place\n // instead of a copy-pasted `config?.x ?? deps.connectDefaults.x ?? fallback` per option.\n function resolve<Opt extends keyof ConnectOptions>(option: Opt, fallback: NonNullable<ConnectOptions[Opt]>) {\n return config?.[option] ?? deps.connectDefaults[option] ?? fallback;\n }\n\n const touchOnBlur = resolve('touchOnBlur', false);\n const validateOnBlur = resolve('validateOnBlur', false);\n const validateOnChange = resolve('validateOnChange', false);\n const validateOnTouch = resolve('validateOnTouch', false);\n const debounceMs = resolve('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"],"mappings":";;;;AA+BA,SAAgB,EACd,GACA,GACA;CACA,SAAS,EAAU,GAAsC,GAAyC;EAChG,OAAO,EAAI,UAAU,GAAU,CAAO;CACxC;CAEA,SAAS,EACP,GACA,GACA,GACa;EACb,IAAM,IAAM;EAIZ,OAFA,EAAc,CAAG,GAEV,EAAI,eAAe,GAAK,GAAkD,CAAO;CAC1F;CAIA,SAAS,EACP,GACA,GAC0C;EAC1C,EAAI,kBAAkB,SAAS;EAE/B,IAAM,IAAM;EAEZ,EAAc,CAAG;EAIjB,SAAS,EAA0C,GAAa,GAA4C;GAC1G,OAAO,IAAS,MAAW,EAAK,gBAAgB,MAAW;EAC7D;EAEA,IAAM,IAAc,EAAQ,eAAe,EAAK,GAC1C,IAAiB,EAAQ,kBAAkB,EAAK,GAChD,IAAmB,EAAQ,oBAAoB,EAAK,GACpD,IAAkB,EAAQ,mBAAmB,EAAK,GAClD,IAAa,EAAQ,YAAY,CAAC,GAGpC,IAAsD;EAE1D,SAAS,IAA2B;GAClC,AAAI,IAAa,KACX,MAAkB,QAAM,aAAa,CAAa,GAEtD,IAAgB,iBAAiB;IAC/B,IAAgB,MAEZ,GAAI,YAER,EAAU,eAAe,CAAC,CAAI,CAAC,CAAC,CAAC,OAAO,MAAiB;KACvD,IAAI,CAAC,EAAa,CAAG,GAAG,MAAM;IAChC,CAAC;GACH,GAAG,CAAU,KAEb,EAAU,eAAe,CAAC,CAAI,CAAC,CAAC,CAAC,OAAO,MAAiB;IACvD,IAAI,CAAC,EAAa,CAAG,GAAG,MAAM;GAChC,CAAC;EAEL;EAEA,IAAI,IAAkB;EAEtB,OAAO;GACL,IAAI,QAAQ;IACV,OAAO,EAAI,MAAM,IAAI,CAAG;GAC1B;GAEA,UAAU;IAGR,AAFA,IAAkB,IAEd,MAAkB,SACpB,aAAa,CAAa,GAC1B,IAAgB;GAEpB;GACA,IAAI,WAAW;IACb,OAAO;GACT;GACA,IAAI,QAAQ;IACV,OAAO,EAAI,YAAY,IAAI,CAAG;GAChC;GACA,cAAc;IACR,EAAI,aAEJ,KAAa,EAAK,MAAM,CAAI,GAE5B,KAAgB,EAAmB;GACzC;GACA,WAAW,MAAkC;IACvC,EAAI,aAER,EAAK,IAAI,GAAM,CAAK,IAEG,KAAqB,KAAmB,EAAI,QAAQ,IAAI,CAAG,MAE9D,EAAmB;GACzC;GACA,CAAC,OAAO,WAAW;IACjB,KAAK,QAAQ;GACf;GACA,IAAI,UAAU;IACZ,OAAO,EAAI,QAAQ,IAAI,CAAG;GAC5B;GACA,IAAI,QAAQ;IACV,OAAO,EAAI,MAAM,IAAI,CAAG;GAC1B;EACF;CACF;CAIA,SAAS,IAAwD;EAC/D,IAAM,IAAQ,QAAkC,EAAY,CAAC;EAE7D,EAAM,KAAK,EAAI,iBAAiB,CAAC;EAEjC,IAAM,IAAc,GAAW,MAAU;GACvC,EAAM,KAAK,CAAK;EAClB,CAAC;EAID,OAFA,EAAI,kBAAkB,OAAO,iBAAiB,eAAe,EAAM,UAAU,GAAG,EAAE,MAAM,GAAK,CAAC,GAEvF,EAAM;CACf;CAEA,OAAO;EAAE;EAAS;EAAW;GAAiB,OAAO,gBAAgB;CAAoB;AAC3F"}
@@ -1,2 +0,0 @@
1
- const e=require("../errors.cjs");require("../_utils.cjs");let t=require("@vielzeug/arsenal");function n(r,i){let{ctx:a,fieldOps:o,root:s}=r,c=`${i}.`;function l(e){return`${i}.${e}`}function u(e){return e===i||e.startsWith(c)}function d(e){return e.startsWith(c)?e.slice(c.length):e}function f(){let e=a.getStateSnapshot(),t=Object.fromEntries(Object.entries(e.errors).filter(([e])=>u(e)).map(([e,t])=>[d(e),t])),n=e.touchedFields.filter(u).map(d),r=e.validatingFields.filter(u).map(d),i=Object.keys(t).length===0,o=[...a.dirty].some(u),s=[...a.touched].some(u),c=r.length>0;return Object.freeze({errors:Object.freeze(t),isDirty:o,isLoading:e.isLoading,isSubmitting:e.isSubmitting,isTouched:s,isValid:i,isValidating:c,submitCount:e.submitCount,touchedFields:Object.freeze(n),validatingFields:Object.freeze(r)})}function p(){let e={};for(let[t,n]of a.store)t.startsWith(c)&&(e[t.slice(c.length)]=n);return(0,t.unflattenPaths)(e)}function m(){let e=new Set;for(let t of a.store.keys())u(t)&&e.add(d(t));for(let t of a.validators.keys())u(t)&&e.add(d(t));return Object.freeze([...e])}function h(){a.ensureNotDisposed(`touchAll`),o.touchAllKeys(u),a.requestNotify()}function g(){a.ensureNotDisposed(`untouchAll`),o.untouchAllKeys(u),a.requestNotify()}function _(){a.ensureNotDisposed(`reset`),o.resetKeys(u),a.requestNotify()}function v(e){a.ensureNotDisposed(`replace`),o.replaceKeys(u,(0,t.flattenPaths)(e),l),a.requestNotify()}function y(e){a.ensureNotDisposed(`patch`),a.requestNotify(o.patchKeys(e,l))}function b(e){a.ensureNotDisposed(`resetErrors`),o.resetErrorsKeys(u,e??{},l),a.requestNotify()}async function x(e,t){if(a.ensureNotDisposed(`validate`),e!==void 0&&!Array.isArray(e)&&!(e instanceof AbortSignal)){let n=l(e);await o.runValidationCore([n],`partial`,t);let r=a.fieldErrors.get(n);return{errors:r===void 0?{}:{[e]:r},valid:r===void 0}}if(Array.isArray(e)){let n=e.map(e=>l(e));await o.runValidationCore(n,`partial`,t);let r={};for(let e of n){let t=a.fieldErrors.get(e);t!==void 0&&(r[d(e)]=t)}return{errors:r,valid:Object.keys(r).length===0}}let n=e,r=[...a.validators.keys()].filter(u);await o.runValidationCore(r,`partial`,n);let i={};for(let[e,t]of a.fieldErrors)u(e)&&(i[d(e)]=t);return{errors:i,valid:Object.keys(i).length===0}}async function S(t){if(a.ensureNotDisposed(`submit`),a.isSubmittingState)throw new e.ForgeSubmitError(`submit() called while a submission is already in progress`);a.batch(()=>{a.submitCount++,a.isSubmittingState=!0,h()});try{let e=[...a.validators.keys()].filter(u);await o.runValidationCore(e,`partial`),a.ensureNotDisposed(`submit`);let n={};for(let[e,t]of a.fieldErrors)u(e)&&(n[d(e)]=t);return Object.keys(n).length>0?{errors:n,ok:!1,type:`validation`}:{ok:!0,value:await t(p())}}finally{a.isSubmittingState=!1,a.disposed||a.requestNotify()}}function C(e,t){let n=null;return s.subscribe(()=>{let t=f();if(n){let e=n,r=Object.keys(t.errors),i=Object.keys(e.errors),a=r.length===i.length&&r.every(n=>t.errors[n]===e.errors[n]),o=t.touchedFields.length===e.touchedFields.length&&t.touchedFields.every((t,n)=>t===e.touchedFields[n]),s=t.validatingFields.length===e.validatingFields.length&&t.validatingFields.every((t,n)=>t===e.validatingFields[n]);if(t.isDirty===e.isDirty&&t.isLoading===e.isLoading&&t.isSubmitting===e.isSubmitting&&t.isTouched===e.isTouched&&t.isValid===e.isValid&&t.isValidating===e.isValidating&&t.submitCount===e.submitCount&&a&&o&&s)return}n=t,e(t)},t)}function w(){return{array(e){return s.array(l(e))},clearError(e){s.clearError(l(e))},connect(e,t){return s.connect(l(e),t)},field(e){return s.field(l(e))},get(e){return s.get(l(e))},register(e,t){return s.fields.register(l(e),t)},remove(e){s.fields.remove(l(e))},resetField(e){s.resetField(l(e))},set(e,t,n){s.set(l(e),t,n)},setError(e,t){s.setError(l(e),t)},setValidator(e,t){s.fields.setValidator(l(e),t)},subscribeField(e,t,n){return s.subscribeField(l(e),t,n)},touch(e){s.touch(l(e))},untouch(e){s.untouch(l(e))}}}let T=w();return{array:e=>T.array(e),batch:e=>s.batch(e),clearError:e=>T.clearError(e),connect:(e,t)=>T.connect(e,t),get disposalSignal(){return s.disposalSignal},dispose:()=>{},get disposed(){return a.disposed},field:e=>T.field(e),fields:{list:m,register:(e,t)=>T.register(e,t),remove:e=>T.remove(e),setValidator:(e,t)=>T.setValidator(e,t)},get:e=>T.get(e),history:{restore:e=>s.history.restore(e),snapshot:()=>s.history.snapshot()},get isLoading(){return s.isLoading},get isSubmitting(){return s.isSubmitting},patch:y,replace:v,reset:_,resetErrors:b,resetField:e=>T.resetField(e),scope:e=>n(r,l(e)),set:(e,t,n)=>T.set(e,t,n),setError:(e,t)=>T.setError(e,t),get state(){return f()},submit:S,submitOrThrow:async t=>{let n=await S(t);if(!n.ok)throw new e.ForgeValidationError(n.errors);return n.value},subscribe:C,subscribeField:(e,t,n)=>T.subscribeField(e,t,n),[Symbol.asyncIterator]:()=>s[Symbol.asyncIterator](),[Symbol.dispose](){this.dispose()},touch:e=>T.touch(e),touchAll:h,untouch:e=>T.untouch(e),untouchAll:g,validate:x,values:p}}exports.createScopedForm=n;
2
- //# sourceMappingURL=scope.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scope.cjs","names":[],"sources":["../../src/core/scope.ts"],"sourcesContent":["import type {\n ArrayField,\n ConnectOptions,\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';\nimport type { FormContext } from './context';\nimport type { FieldOps } from './fields';\n\nimport { flattenValues, unflattenValues } from '../_utils';\nimport { ForgeSubmitError, ForgeValidationError } from '../errors';\n\n/**\n * Everything a scoped sub-form needs from its parent. Deliberately just three members —\n * `ctx` (the same `FormContext` the root form's own ops read/write, not a copy of its\n * fields) and a narrow slice of `fieldOps`'s predicate-based bulk primitives\n * (`reset`/`replace`/`patch`/`touchAll`/`untouchAll`/`resetErrors`'s shared implementations,\n * plus `runValidationCore`). Bulk scoped operations call these with an `isScopedKey`\n * predicate instead of maintaining a second copy of each loop.\n * @internal\n */\nexport interface ScopeContext<TValues extends Record<string, unknown> = Record<string, unknown>> {\n ctx: FormContext<TValues>;\n fieldOps: Pick<\n FieldOps<TValues>,\n | 'patchKeys'\n | 'replaceKeys'\n | 'resetErrorsKeys'\n | 'resetKeys'\n | 'runValidationCore'\n | 'touchAllKeys'\n | 'untouchAllKeys'\n >;\n /** The fully-featured root form — used for all simple one-field delegations. */\n root: Form<TValues>;\n}\n\n/**\n * Creates a scoped sub-form whose field paths are relative to `pfx`.\n * All simple single-field operations delegate to `scopeCtx.root`. Bulk operations\n * (reset, replace, patch, touchAll, untouchAll, resetErrors) call `scopeCtx.fieldOps`'s\n * shared predicate-based primitives with an `isScopedKey` filter. Only `validate`/`submit`\n * (which need to compute a scoped field-name subset before calling `runValidationCore`) and\n * the handful of read-only projections (`state`, `values`, `fields.list`) contain custom code.\n */\nexport function createScopedForm<TValues extends Record<string, unknown>, P extends string>(\n scopeCtx: ScopeContext<TValues>,\n pfx: P,\n): Form<ScopedValues<TValues, P>> {\n const { ctx, fieldOps, root } = scopeCtx;\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('touchAll');\n fieldOps.touchAllKeys(isScopedKey);\n ctx.requestNotify();\n }\n\n function scopedUntouchAll(): void {\n ctx.ensureNotDisposed('untouchAll');\n fieldOps.untouchAllKeys(isScopedKey);\n ctx.requestNotify();\n }\n\n function scopedReset(): void {\n ctx.ensureNotDisposed('reset');\n fieldOps.resetKeys(isScopedKey);\n ctx.requestNotify();\n }\n\n function scopedReplace(newValues: ScopedValues<TValues, P>): void {\n ctx.ensureNotDisposed('replace');\n fieldOps.replaceKeys(isScopedKey, flattenValues(newValues as Record<string, unknown>), pre);\n ctx.requestNotify();\n }\n\n function scopedPatch(partial: Record<string, unknown>): void {\n ctx.ensureNotDisposed('patch');\n ctx.requestNotify(fieldOps.patchKeys(partial, pre));\n }\n\n function scopedResetErrors(\n nextErrors?: Partial<Record<ErrorKeyOf<ScopedValues<TValues, P>>, string | undefined>>,\n ): void {\n ctx.ensureNotDisposed('resetErrors');\n fieldOps.resetErrorsKeys(isScopedKey, nextErrors ?? {}, pre);\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('validate');\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 fieldOps.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 fieldOps.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 fieldOps.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('submit');\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 scopedTouchAll();\n });\n\n try {\n const fields = [...ctx.validators.keys()].filter(isScopedKey);\n\n await fieldOps.runValidationCore(fields, 'partial');\n\n ctx.ensureNotDisposed('submit');\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.isSubmittingState = false;\n\n if (!ctx.disposed) ctx.requestNotify();\n }\n }\n\n /**\n * Subscribe filtered to scoped fields only — this scoped form's `subscribe()` implementation.\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 scopedSubscribe(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 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 function createScopedAdapter() {\n return {\n array(name: string): ArrayField {\n return root.array(pre(name) as FlatKeyOf<TValues>) as ArrayField;\n },\n clearError(name: string): void {\n root.clearError(pre(name) as ErrorKeyOf<TValues>);\n },\n connect(name: string, config?: ConnectOptions): ConnectionResult<unknown> {\n return root.connect(pre(name) as FlatKeyOf<TValues>, config) as ConnectionResult<unknown>;\n },\n field(name: string): FieldState<unknown> {\n return root.field(pre(name) as FlatKeyOf<TValues>) as FieldState<unknown>;\n },\n get(name: string): unknown {\n return root.get(pre(name) as FlatKeyOf<TValues>);\n },\n register(name: string, options?: RegisterFieldOptions<unknown>): Unsubscribe {\n return root.fields.register(\n pre(name) as FlatKeyOf<TValues>,\n options as RegisterFieldOptions<TypeAtPath<TValues, FlatKeyOf<TValues>>>,\n );\n },\n remove(name: string): void {\n root.fields.remove(pre(name) as FlatKeyOf<TValues>);\n },\n resetField(name: string): void {\n root.resetField(pre(name) as FlatKeyOf<TValues>);\n },\n set(name: string, value: unknown, options?: SetOptions): void {\n root.set(pre(name) as FlatKeyOf<TValues>, value as TypeAtPath<TValues, FlatKeyOf<TValues>>, options);\n },\n setError(name: string, message: string): void {\n root.setError(pre(name) as ErrorKeyOf<TValues>, message);\n },\n setValidator(name: string, validator?: FieldValidator): void {\n root.fields.setValidator(pre(name) as FlatKeyOf<TValues>, validator);\n },\n subscribeField(\n name: string,\n listener: (state: FieldState<unknown>) => void,\n options?: SubscribeOptions,\n ): Unsubscribe {\n return root.subscribeField(\n pre(name) as FlatKeyOf<TValues>,\n listener as (state: FieldState<TypeAtPath<TValues, FlatKeyOf<TValues>>>) => void,\n options,\n );\n },\n touch(name: string): void {\n root.touch(pre(name) as FlatKeyOf<TValues>);\n },\n untouch(name: string): void {\n root.untouch(pre(name) as FlatKeyOf<TValues>);\n },\n };\n }\n\n /* ---- Public scoped form object ---- */\n\n type S = ScopedValues<TValues, P>;\n\n const adapter = createScopedAdapter();\n\n return {\n array: (name) =>\n adapter.array(name as string) as ArrayField<\n TypeAtPath<S, typeof name> extends readonly (infer E)[] ? E : unknown\n >,\n batch: (fn) => root.batch(fn),\n clearError: (name) => adapter.clearError(name as string),\n connect: (name, config?) => adapter.connect(name as string, config) as ConnectionResult<TypeAtPath<S, typeof name>>,\n get disposalSignal() {\n return 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.disposed;\n },\n field: (name) => adapter.field(name as string) as FieldState<TypeAtPath<S, typeof name>>,\n fields: {\n list: scopedListFields,\n register: (name, options?) => adapter.register(name as string, options as RegisterFieldOptions<unknown>),\n remove: (name) => adapter.remove(name as string),\n setValidator: (name, validator?) => adapter.setValidator(name as string, validator),\n },\n get: (name) => adapter.get(name as string) as TypeAtPath<S, typeof name>,\n history: {\n // Casts are irreducible: FormSnapshot<S> and FormSnapshot<TValues> are structurally\n // unrelated generic instantiations -- TS cannot verify one against the other.\n restore: (snap) => root.history.restore(snap as FormSnapshot<TValues>),\n snapshot: () => root.history.snapshot() as FormSnapshot<S>,\n },\n get isLoading() {\n return root.isLoading;\n },\n get isSubmitting() {\n return 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) => adapter.resetField(name as string),\n scope: (subPrefix) =>\n createScopedForm(scopeCtx, pre(subPrefix as string)) as Form<ScopedValues<S, typeof subPrefix>>,\n set: (name, value, options?: SetOptions) => adapter.set(name as string, value, options),\n setError: (name, message) => adapter.setError(name as string, message),\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 // Scoped forms filter+remap; on a root form this method is the unfiltered notifier pass-through.\n subscribe: scopedSubscribe,\n subscribeField: (name, listener, options?) =>\n adapter.subscribeField(name as string, listener as (state: FieldState<unknown>) => void, options),\n [Symbol.asyncIterator]: () => root[Symbol.asyncIterator](),\n [Symbol.dispose]() {\n this.dispose();\n },\n touch: (name) => adapter.touch(name as string),\n touchAll: scopedTouchAll,\n untouch: (name) => adapter.untouch(name as string),\n untouchAll: scopedUntouchAll,\n validate: scopedValidate as Form<S>['validate'],\n values: scopedValues,\n };\n}\n"],"mappings":"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"}
@@ -1,11 +0,0 @@
1
- import type { Form, ScopedValues } from '../types';
2
- /**
3
- * Creates a scoped sub-form whose field paths are relative to `pfx`.
4
- * All simple single-field operations delegate to `scopeCtx.root`. Bulk operations
5
- * (reset, replace, patch, touchAll, untouchAll, resetErrors) call `scopeCtx.fieldOps`'s
6
- * shared predicate-based primitives with an `isScopedKey` filter. Only `validate`/`submit`
7
- * (which need to compute a scoped field-name subset before calling `runValidationCore`) and
8
- * the handful of read-only projections (`state`, `values`, `fields.list`) contain custom code.
9
- */
10
- export declare function createScopedForm<TValues extends Record<string, unknown>, P extends string>(scopeCtx: ScopeContext<TValues>, pfx: P): Form<ScopedValues<TValues, P>>;
11
- //# sourceMappingURL=scope.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/core/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAQV,IAAI,EAKJ,YAAY,EAOb,MAAM,UAAU,CAAC;AAgClB;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,EACxF,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,EAC/B,GAAG,EAAE,CAAC,GACL,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CA4YhC"}