@effect-app/vue-components 4.0.0-beta.158 → 4.0.0-beta.159

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 (124) hide show
  1. package/dist/types/components/OmegaForm/OmegaArray.vue.d.ts +1 -1
  2. package/dist/types/components/OmegaForm/OmegaAutoGen.vue.d.ts +1 -1
  3. package/dist/types/components/OmegaForm/OmegaErrorsInternal.vue.d.ts +1 -1
  4. package/dist/types/components/OmegaForm/OmegaFormInput.vue.d.ts +1 -1
  5. package/dist/types/components/OmegaForm/OmegaInput.vue.d.ts +1 -1
  6. package/dist/types/components/OmegaForm/OmegaInternalInput.vue.d.ts +2 -1
  7. package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +1 -1
  8. package/dist/types/components/OmegaForm/createUseFormWithCustomInput.d.ts +2 -2
  9. package/dist/types/components/OmegaForm/errors.d.ts +33 -0
  10. package/dist/types/components/OmegaForm/getOmegaStore.d.ts +1 -1
  11. package/dist/types/components/OmegaForm/hocs.d.ts +3 -0
  12. package/dist/types/components/OmegaForm/index.d.ts +13 -3
  13. package/dist/types/components/OmegaForm/inputs.d.ts +4 -0
  14. package/dist/types/components/OmegaForm/meta/checks.d.ts +4 -0
  15. package/dist/types/components/OmegaForm/meta/createMeta.d.ts +32 -0
  16. package/dist/types/components/OmegaForm/meta/defaults.d.ts +2 -0
  17. package/dist/types/components/OmegaForm/meta/redacted.d.ts +2 -0
  18. package/dist/types/components/OmegaForm/meta/types.d.ts +56 -0
  19. package/dist/types/components/OmegaForm/meta/walker.d.ts +18 -0
  20. package/dist/types/components/OmegaForm/persistency.d.ts +58 -0
  21. package/dist/types/components/OmegaForm/submit.d.ts +60 -0
  22. package/dist/types/components/OmegaForm/types.d.ts +281 -0
  23. package/dist/types/components/OmegaForm/useOmegaForm.d.ts +6 -212
  24. package/dist/types/components/OmegaForm/validation/localized.d.ts +10 -0
  25. package/dist/vue-components.es.js +24 -16
  26. package/dist/vue-components10.es.js +4 -4
  27. package/dist/vue-components11.es.js +19 -12
  28. package/dist/vue-components12.es.js +22 -444
  29. package/dist/vue-components13.es.js +126 -3
  30. package/dist/vue-components14.es.js +61 -34
  31. package/dist/vue-components15.es.js +57 -24
  32. package/dist/vue-components16.es.js +20 -26
  33. package/dist/vue-components17.es.js +4 -6
  34. package/dist/vue-components18.es.js +78 -16
  35. package/dist/vue-components19.es.js +86 -30
  36. package/dist/vue-components20.es.js +72 -17
  37. package/dist/vue-components21.es.js +10 -19
  38. package/dist/vue-components22.es.js +54 -28
  39. package/dist/vue-components23.es.js +4 -6
  40. package/dist/vue-components24.es.js +43 -8
  41. package/dist/vue-components25.es.js +4 -37
  42. package/dist/vue-components26.es.js +83 -24
  43. package/dist/vue-components28.es.js +6 -22
  44. package/dist/vue-components29.es.js +8 -20
  45. package/dist/vue-components3.es.js +2 -2
  46. package/dist/vue-components30.es.js +267 -7
  47. package/dist/vue-components32.es.js +7 -4
  48. package/dist/vue-components33.es.js +71 -27
  49. package/dist/vue-components34.es.js +4 -4
  50. package/dist/vue-components35.es.js +50 -27
  51. package/dist/vue-components36.es.js +4 -5
  52. package/dist/vue-components37.es.js +23 -17
  53. package/dist/vue-components38.es.js +4 -55
  54. package/dist/vue-components39.es.js +57 -3
  55. package/dist/vue-components40.es.js +4 -43
  56. package/dist/vue-components41.es.js +11 -4
  57. package/dist/vue-components42.es.js +17 -79
  58. package/dist/vue-components44.es.js +8 -7
  59. package/dist/vue-components45.es.js +3 -8
  60. package/dist/vue-components46.es.js +36 -267
  61. package/dist/vue-components47.es.js +27 -0
  62. package/dist/vue-components48.es.js +27 -7
  63. package/dist/vue-components49.es.js +6 -79
  64. package/dist/vue-components50.es.js +17 -4
  65. package/dist/vue-components51.es.js +32 -69
  66. package/dist/vue-components52.es.js +17 -4
  67. package/dist/vue-components53.es.js +19 -22
  68. package/dist/vue-components54.es.js +29 -4
  69. package/dist/vue-components55.es.js +6 -58
  70. package/dist/vue-components56.es.js +8 -4
  71. package/dist/vue-components57.es.js +37 -11
  72. package/dist/vue-components58.es.js +24 -21
  73. package/dist/{vue-components27.es.js → vue-components59.es.js} +2 -2
  74. package/dist/vue-components6.es.js +11 -11
  75. package/dist/vue-components60.es.js +23 -8
  76. package/dist/vue-components61.es.js +18 -232
  77. package/dist/vue-components62.es.js +7 -31
  78. package/dist/vue-components63.es.js +19 -8
  79. package/dist/vue-components64.es.js +4 -35
  80. package/dist/vue-components65.es.js +29 -0
  81. package/dist/vue-components66.es.js +5 -0
  82. package/dist/vue-components67.es.js +29 -0
  83. package/dist/vue-components68.es.js +6 -0
  84. package/dist/vue-components69.es.js +18 -0
  85. package/dist/vue-components7.es.js +11 -26
  86. package/dist/vue-components70.es.js +40 -0
  87. package/dist/vue-components71.es.js +81 -0
  88. package/dist/vue-components72.es.js +33 -0
  89. package/dist/vue-components73.es.js +19 -0
  90. package/dist/vue-components74.es.js +48 -0
  91. package/dist/vue-components8.es.js +33 -45
  92. package/dist/vue-components9.es.js +46 -4
  93. package/package.json +7 -7
  94. package/src/components/CommandButton.vue +3 -1
  95. package/src/components/OmegaForm/OmegaArray.vue +1 -1
  96. package/src/components/OmegaForm/OmegaAutoGen.vue +2 -1
  97. package/src/components/OmegaForm/OmegaErrorsInternal.vue +1 -1
  98. package/src/components/OmegaForm/OmegaFormInput.vue +1 -1
  99. package/src/components/OmegaForm/OmegaInput.vue +6 -68
  100. package/src/components/OmegaForm/OmegaInputVuetify.vue +1 -1
  101. package/src/components/OmegaForm/OmegaInternalInput.vue +5 -11
  102. package/src/components/OmegaForm/OmegaTaggedUnion.vue +2 -1
  103. package/src/components/OmegaForm/OmegaWrapper.vue +1 -1
  104. package/src/components/OmegaForm/blockDialog.ts +10 -1
  105. package/src/components/OmegaForm/createUseFormWithCustomInput.ts +2 -1
  106. package/src/components/OmegaForm/errors.ts +136 -0
  107. package/src/components/OmegaForm/getOmegaStore.ts +1 -1
  108. package/src/components/OmegaForm/hocs.ts +19 -0
  109. package/src/components/OmegaForm/index.ts +16 -4
  110. package/src/components/OmegaForm/inputs.ts +22 -0
  111. package/src/components/OmegaForm/meta/checks.ts +81 -0
  112. package/src/components/OmegaForm/meta/createMeta.ts +138 -0
  113. package/src/components/OmegaForm/meta/defaults.ts +132 -0
  114. package/src/components/OmegaForm/meta/redacted.ts +66 -0
  115. package/src/components/OmegaForm/meta/types.ts +78 -0
  116. package/src/components/OmegaForm/meta/walker.ts +247 -0
  117. package/src/components/OmegaForm/persistency.ts +247 -0
  118. package/src/components/OmegaForm/submit.ts +128 -0
  119. package/src/components/OmegaForm/types.ts +751 -0
  120. package/src/components/OmegaForm/useOmegaForm.ts +49 -913
  121. package/src/components/OmegaForm/validation/localized.ts +202 -0
  122. package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +0 -173
  123. package/dist/vue-components31.es.js +0 -19
  124. package/src/components/OmegaForm/OmegaFormStuff.ts +0 -1422
@@ -1,675 +1,34 @@
1
1
  /* eslint-disable @typescript-eslint/consistent-type-imports */
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
 
4
- import * as api from "@opentelemetry/api"
5
- import { type DeepKeys, DeepValue, type FormAsyncValidateOrFn, type FormValidateOrFn, type StandardSchemaV1, StandardSchemaV1Issue, useForm, ValidationError, ValidationErrorMap } from "@tanstack/vue-form"
6
- import { Array, Context, Data, Effect, Fiber, Option, Order, S } from "effect-app"
7
- import { runtimeFiberAsPromise, UnionToTuples } from "effect-app/utils"
8
- import { Component, computed, ComputedRef, ConcreteComponent, h, type InjectionKey, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from "vue"
9
- import { useIntl } from "../../utils"
10
- import { MergedInputProps } from "./InputProps"
4
+ import { type FormAsyncValidateOrFn, type FormValidateOrFn, revalidateLogic, type StandardSchemaV1, useForm } from "@tanstack/vue-form"
5
+ import { Context, S } from "effect-app"
6
+ import { type InjectionKey, watch } from "vue"
7
+ import { eHoc, makeFieldMap } from "./errors"
8
+ import { fHoc } from "./hocs"
9
+ import { generateMetaFromSchema } from "./meta/createMeta"
10
+ import { defaultsValueFromSchema } from "./meta/defaults"
11
+ import { toFormSchema } from "./meta/redacted"
11
12
  import OmegaArray from "./OmegaArray.vue"
12
13
  import OmegaAutoGen from "./OmegaAutoGen.vue"
13
14
  import OmegaErrorsInternal from "./OmegaErrorsInternal.vue"
14
- import { BaseProps, deepMerge, defaultsValueFromSchema, DefaultTypeProps, FieldPath, type FormProps, generateMetaFromSchema, type MetaRecord, type NestedKeyOf, OmegaArrayProps, OmegaAutoGenMeta, OmegaError, type OmegaFormApi, OmegaFormState, toFormSchema } from "./OmegaFormStuff"
15
15
  import OmegaInput from "./OmegaInput.vue"
16
16
  import OmegaTaggedUnion from "./OmegaTaggedUnion.vue"
17
17
  import OmegaForm from "./OmegaWrapper.vue"
18
+ import { usePersistency } from "./persistency"
19
+ import { makeSubmitHandlers, wrapOnSubmit } from "./submit"
20
+ import type { DefaultTypeProps, FormProps, OF, OmegaConfig, OmegaFormApi, OmegaFormReturn } from "./types"
21
+ import { annotateLiteralUnionMessages, toLocalizedStandardSchemaV1 } from "./validation/localized"
18
22
 
19
23
  import { makeRunPromise } from "@effect-app/vue/runtime"
24
+ import { useIntl } from "../../utils"
20
25
 
21
- type keysRule<T> =
22
- | {
23
- keys?: NestedKeyOf<T>[]
24
- banKeys?: "You should only use one of banKeys or keys, not both, moron"
25
- }
26
- | {
27
- keys?: "You should only use one of banKeys or keys, not both, moron"
28
- banKeys?: NestedKeyOf<T>[]
29
- }
30
-
31
- export class FormErrors<From> extends Data.TaggedError("FormErrors")<{
32
- form: {
33
- // TODO: error shapes seem off, with `undefined` etc..
34
- errors: (Record<string, StandardSchemaV1Issue[]> | undefined)[]
35
- errorMap: ValidationErrorMap<
36
- undefined,
37
- undefined,
38
- Record<string, StandardSchemaV1Issue[]>,
39
- undefined,
40
- undefined,
41
- undefined,
42
- undefined,
43
- undefined,
44
- undefined,
45
- undefined
46
- >
47
- }
48
- fields: Record<DeepKeys<From>, {
49
- errors: ValidationError[]
50
- errorMap: ValidationErrorMap
51
- }>
52
- }> {}
53
-
54
- const fHoc = (form: OF<any, any>) => {
55
- return function FormHoc<P>(
56
- WrappedComponent: Component<P>
57
- ): ConcreteComponent<P> {
58
- return {
59
- render() {
60
- return h(WrappedComponent, {
61
- form,
62
- ...this.$attrs
63
- } as any, this.$slots)
64
- }
65
- }
66
- }
67
- }
68
-
69
- export const useErrorLabel = (form: OF<any, any>) => {
70
- const { formatMessage } = useIntl()
71
- const humanize = (str: string) => {
72
- return str
73
- .replace(/([A-Z])/g, " $1") // Add space before capital letters
74
- .replace(/^./, (char) => char.toUpperCase()) // Capitalize the first letter
75
- .trim() // Remove leading/trailing spaces
76
- }
77
- const fallback = (propsName: string) =>
78
- formatMessage
79
- ? formatMessage({ id: `general.fields.${propsName}`, defaultMessage: humanize(propsName) })
80
- : humanize(propsName)
81
- const i18n = (propsName: string) =>
82
- form.i18nNamespace
83
- ? formatMessage({ id: `${form.i18nNamespace}.fields.${propsName}`, defaultMessage: fallback(propsName) })
84
- : fallback(propsName)
85
-
86
- return i18n
87
- }
88
-
89
- const eHoc = (errorProps: {
90
- form: OF<any, any>
91
- fieldMap: Ref<Map<string, { id: string; label: string }>>
92
- }) => {
93
- return function FormHoc<P>(
94
- WrappedComponent: Component<P>
95
- ): ConcreteComponent<P> {
96
- return {
97
- setup() {
98
- const { fieldMap, form } = errorProps
99
- const generalErrors = form.useStore((state) => state.errors)
100
- const fieldMeta = form.useStore((state) => state.fieldMeta)
101
- const errorMap = form.useStore((state) => state.errorMap)
102
-
103
- const errorLabel = useErrorLabel(form)
104
-
105
- const errors = computed(() => {
106
- // Collect errors from fieldMeta (field-level errors for registered fields)
107
- const fieldErrors = Object.entries(fieldMeta.value).reduce<OmegaError[]>((acc, [key, m]) => {
108
- const fieldErrors = (m as { errors?: Array<{ message?: string }> } | undefined)?.errors ?? []
109
- if (!fieldErrors.length) {
110
- return acc
111
- }
112
-
113
- const fieldInfo = fieldMap.value.get(key)
114
- if (!fieldInfo) {
115
- return acc
116
- }
117
-
118
- acc.push({
119
- label: fieldInfo.label,
120
- inputId: fieldInfo.id,
121
- errors: [fieldErrors[0]?.message].filter(Boolean) as string[]
122
- })
123
-
124
- return acc
125
- }, [])
126
-
127
- // Collect errors from errorMap.onSubmit ONLY for fields that are NOT registered
128
- // (registered fields already have their errors in fieldMeta)
129
- const submitErrors: OmegaError[] = []
130
- if (errorMap.value.onSubmit) {
131
- for (const [_, issues] of Object.entries(errorMap.value.onSubmit)) {
132
- if (Array.isArray(issues) && issues.length) {
133
- for (const issue of issues) {
134
- const issAny: any = issue
135
- if (issAny?.path && Array.isArray(issAny.path) && issAny.path.length) {
136
- // Use the path from the issue to identify the field
137
- const fieldPath = issAny.path.join(".")
138
- // Only add errors for fields that are NOT registered (not in fieldMap)
139
- // Registered fields will already have their errors from fieldMeta
140
- if (!fieldMap.value.has(fieldPath)) {
141
- submitErrors.push({
142
- label: errorLabel(fieldPath),
143
- inputId: fieldPath,
144
- errors: [issAny.message].filter(Boolean)
145
- })
146
- // Only show first error per field, so break after adding
147
- break
148
- }
149
- }
150
- }
151
- }
152
- }
153
- }
154
-
155
- // Combine both error sources (no need to check for duplicates since they're mutually exclusive)
156
- return [...fieldErrors, ...submitErrors]
157
- })
158
-
159
- return {
160
- generalErrors,
161
- errors
162
- }
163
- },
164
- render({ errors, generalErrors }: any) {
165
- return h(WrappedComponent, {
166
- errors,
167
- generalErrors,
168
- ...this.$attrs
169
- } as any, this.$slots)
170
- }
171
- }
172
- }
173
- }
174
-
175
- export type Policies = "local" | "session" | "querystring"
176
- export type defaultValuesPriorityUnion = "tanstack" | "persistency" | "schema"
177
-
178
- const includesPolicy = (arr: Policies[], policy: Policies) => {
179
- return arr.includes(policy)
180
- }
181
-
182
- export type OmegaConfig<T> = {
183
- i18nNamespace?: string
184
-
185
- persistency?: {
186
- /** Order of importance:
187
- * - "querystring": Highest priority when persisting
188
- * - "local" and then "session": Lower priority storage options
189
- */
190
- policies?: UnionToTuples<Policies>
191
- overrideDefaultValues?: "deprecated: use defaultValuesPriority"
192
- id?: string
193
- } & keysRule<T>
194
-
195
- ignorePreventCloseEvents?: boolean
196
-
197
- /**
198
- * Prevents browser window/tab exit when form has unsaved changes.
199
- * Shows native browser "Leave site?" dialog.
200
- *
201
- * @remarks
202
- * - Opt-in only: Must explicitly enable
203
- * - Independent from data persistence feature
204
- */
205
- preventWindowExit?: "prevent" | "prevent-and-reset" | "nope"
206
-
207
- input?: any
208
-
209
- /**
210
- * Default values order is: Tanstack default values passed as second parameter to useOmegaForm, then persistency
211
- * default values from querystring or local/session storage, then defaults from schema
212
- * You can customize the order and with omegaConfig.defaultValuesPriority
213
- * default value = ['tanstack', 'persistency', 'schema']
214
- */
215
- defaultValuesPriority?: UnionToTuples<defaultValuesPriorityUnion>
216
-
217
- defaultFromSchema?: "deprecated: use defaultValuesPriority"
218
- }
219
-
220
- export interface OF<From, To> extends OmegaFormApi<From, To> {
221
- meta: MetaRecord<From>
222
- unionMeta: Record<string, MetaRecord<From>>
223
- clear: () => void
224
- i18nNamespace?: string
225
- ignorePreventCloseEvents?: boolean
226
- registerField: (
227
- field: ComputedRef<{
228
- name: string
229
- label: string
230
- id: string
231
- }>
232
- ) => void
233
- /** @experimental */
234
- handleSubmitEffect: {
235
- /**
236
- * when `checkErrors` is true, the Effect will fail with `FormErrors<From>` when there are validation errors
237
- * @experimental */
238
- (options: { checkErrors: true; meta?: Record<string, any> }): Effect.Effect<void, FormErrors<From>>
239
- /** @experimental */
240
- (options?: { meta?: Record<string, any> }): Effect.Effect<void>
241
- }
242
- }
26
+ export { useErrorLabel } from "./errors"
27
+ export { FormErrors } from "./submit"
28
+ export type { defaultValuesPriorityUnion, OF, OmegaConfig, OmegaFormReturn, Policies } from "./types"
243
29
 
244
30
  export const OmegaFormKey = Symbol("OmegaForm") as InjectionKey<OF<any, any>>
245
31
 
246
- type __VLS_PrettifyLocal<T> =
247
- & {
248
- [K in keyof T]: T[K]
249
- }
250
- & {}
251
-
252
- // Type aliases for Array component slots - using cached types for performance
253
- type CachedFieldApi<From, To, TypeProps = DefaultTypeProps> = import("@tanstack/vue-form").FieldApi<
254
- From,
255
- OmegaFormReturn<From, To, TypeProps>["_keys"],
256
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>,
257
- | import("@tanstack/vue-form").FieldValidateOrFn<
258
- From,
259
- OmegaFormReturn<From, To, TypeProps>["_keys"],
260
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
261
- >
262
- | undefined,
263
- | import("@tanstack/vue-form").FieldValidateOrFn<
264
- From,
265
- OmegaFormReturn<From, To, TypeProps>["_keys"],
266
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
267
- >
268
- | undefined,
269
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
270
- From,
271
- OmegaFormReturn<From, To, TypeProps>["_keys"],
272
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
273
- >
274
- | undefined,
275
- | import("@tanstack/vue-form").FieldValidateOrFn<
276
- From,
277
- OmegaFormReturn<From, To, TypeProps>["_keys"],
278
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
279
- >
280
- | undefined,
281
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
282
- From,
283
- OmegaFormReturn<From, To, TypeProps>["_keys"],
284
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
285
- >
286
- | undefined,
287
- | import("@tanstack/vue-form").FieldValidateOrFn<
288
- From,
289
- OmegaFormReturn<From, To, TypeProps>["_keys"],
290
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
291
- >
292
- | undefined,
293
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
294
- From,
295
- OmegaFormReturn<From, To, TypeProps>["_keys"],
296
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
297
- >
298
- | undefined,
299
- | import("@tanstack/vue-form").FieldValidateOrFn<
300
- From,
301
- OmegaFormReturn<From, To, TypeProps>["_keys"],
302
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
303
- >
304
- | undefined,
305
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
306
- From,
307
- OmegaFormReturn<From, To, TypeProps>["_keys"],
308
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
309
- >
310
- | undefined,
311
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
312
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
313
- import("@tanstack/vue-form").StandardSchemaV1<From, To>,
314
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
315
- import("@tanstack/vue-form").FormAsyncValidateOrFn<From> | undefined,
316
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
317
- import("@tanstack/vue-form").FormAsyncValidateOrFn<From> | undefined,
318
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
319
- import("@tanstack/vue-form").FormAsyncValidateOrFn<From> | undefined,
320
- import("@tanstack/vue-form").FormAsyncValidateOrFn<From> | undefined,
321
- Record<string, any> | undefined
322
- >
323
-
324
- type CachedFieldState<From, To, TypeProps = DefaultTypeProps> = import("@tanstack/vue-form").FieldState<
325
- From,
326
- OmegaFormReturn<From, To, TypeProps>["_keys"],
327
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>,
328
- | import("@tanstack/vue-form").FieldValidateOrFn<
329
- From,
330
- OmegaFormReturn<From, To, TypeProps>["_keys"],
331
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
332
- >
333
- | undefined,
334
- | import("@tanstack/vue-form").FieldValidateOrFn<
335
- From,
336
- OmegaFormReturn<From, To, TypeProps>["_keys"],
337
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
338
- >
339
- | undefined,
340
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
341
- From,
342
- OmegaFormReturn<From, To, TypeProps>["_keys"],
343
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
344
- >
345
- | undefined,
346
- | import("@tanstack/vue-form").FieldValidateOrFn<
347
- From,
348
- OmegaFormReturn<From, To, TypeProps>["_keys"],
349
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
350
- >
351
- | undefined,
352
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
353
- From,
354
- OmegaFormReturn<From, To, TypeProps>["_keys"],
355
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
356
- >
357
- | undefined,
358
- | import("@tanstack/vue-form").FieldValidateOrFn<
359
- From,
360
- OmegaFormReturn<From, To, TypeProps>["_keys"],
361
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
362
- >
363
- | undefined,
364
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
365
- From,
366
- OmegaFormReturn<From, To, TypeProps>["_keys"],
367
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
368
- >
369
- | undefined,
370
- | import("@tanstack/vue-form").FieldValidateOrFn<
371
- From,
372
- OmegaFormReturn<From, To, TypeProps>["_keys"],
373
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
374
- >
375
- | undefined,
376
- | import("@tanstack/vue-form").FieldAsyncValidateOrFn<
377
- From,
378
- OmegaFormReturn<From, To, TypeProps>["_keys"],
379
- DeepValue<From, OmegaFormReturn<From, To, TypeProps>["_keys"]>
380
- >
381
- | undefined,
382
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
383
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
384
- import("@tanstack/vue-form").StandardSchemaV1<From, To>,
385
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
386
- import("@tanstack/vue-form").FormAsyncValidateOrFn<From> | undefined,
387
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
388
- import("@tanstack/vue-form").FormAsyncValidateOrFn<From> | undefined,
389
- import("@tanstack/vue-form").FormValidateOrFn<From> | undefined,
390
- import("@tanstack/vue-form").FormAsyncValidateOrFn<From> | undefined
391
- >
392
-
393
- export interface OmegaFormReturn<
394
- From extends Record<PropertyKey, any>,
395
- To extends Record<PropertyKey, any>,
396
- TypeProps = DefaultTypeProps
397
- > extends OF<From, To> {
398
- // Pre-computed type aliases - computed ONCE for performance
399
- _paths: FieldPath<From>
400
- _keys: NestedKeyOf<From>
401
- _schema: S.Codec<To, From, never>
402
-
403
- // this crazy thing here is copied from the OmegaFormInput.vue.d.ts, with `From` removed as Generic, instead closed over from the From generic above..
404
- Input: <Name extends OmegaFormReturn<From, To, TypeProps>["_paths"]>(
405
- __VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"],
406
- __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>,
407
- __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"],
408
- __VLS_setup?: Promise<{
409
- props:
410
- & __VLS_PrettifyLocal<
411
- & Pick<
412
- & Partial<{}>
413
- & Omit<
414
- {} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps,
415
- never
416
- >,
417
- never
418
- >
419
- & TypeProps
420
- & Partial<{}>
421
- >
422
- & BaseProps<From, Name>
423
- & import("vue").PublicProps
424
- expose(exposed: import("vue").ShallowUnwrapRef<{}>): void
425
- attrs: any
426
- slots: {
427
- default?(props: MergedInputProps<From, Name>): void
428
- label?: (props: { required: boolean; id: string; label: string }) => void
429
- }
430
- emit: {}
431
- }>
432
- ) => import("vue").VNode & {
433
- __ctx?: Awaited<typeof __VLS_setup>
434
- }
435
- Errors: (
436
- __VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"],
437
- __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>,
438
- __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"],
439
- __VLS_setup?: Promise<{
440
- props:
441
- & __VLS_PrettifyLocal<
442
- & Pick<
443
- & Partial<{}>
444
- & Omit<
445
- {} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps,
446
- never
447
- >,
448
- never
449
- >
450
- & Partial<{}>
451
- >
452
- & import("vue").PublicProps
453
- expose(exposed: import("vue").ShallowUnwrapRef<{}>): void
454
- attrs: any
455
- slots: {
456
- default: (props: { errors: readonly OmegaError[]; showedGeneralErrors: string[] }) => void
457
- }
458
- emit: {}
459
- }>
460
- ) => import("vue").VNode & {
461
- __ctx?: Awaited<typeof __VLS_setup>
462
- }
463
- TaggedUnion: <Name extends OmegaFormReturn<From, To, TypeProps>["_keys"]>(
464
- __VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"],
465
- __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>,
466
- __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"],
467
- __VLS_setup?: Promise<{
468
- props:
469
- & __VLS_PrettifyLocal<
470
- & Pick<
471
- & Partial<{}>
472
- & Omit<
473
- {} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps,
474
- never
475
- >,
476
- never
477
- >
478
- & {
479
- name?: Name
480
- type?: "select" | "radio"
481
- options: import("./InputProps").TaggedUnionOptionsArray<From, Name>
482
- _debugName?: [NoInfer<Name>]
483
- label?: string
484
- }
485
- & {}
486
- >
487
- & import("vue").PublicProps
488
- expose(exposed: import("vue").ShallowUnwrapRef<{}>): void
489
- attrs: any
490
- slots: Record<
491
- string,
492
- (props: {
493
- field: import("@tanstack/vue-form").FieldApi<
494
- From,
495
- Name,
496
- DeepValue<From, Name>,
497
- any,
498
- any,
499
- any,
500
- any,
501
- any,
502
- any,
503
- any,
504
- any,
505
- any,
506
- any,
507
- any,
508
- any,
509
- any,
510
- any,
511
- any,
512
- any,
513
- any,
514
- any,
515
- any,
516
- any
517
- >
518
- state: import("@tanstack/vue-form").FieldState<
519
- From,
520
- Name,
521
- DeepValue<From, Name>,
522
- any,
523
- any,
524
- any,
525
- any,
526
- any,
527
- any,
528
- any,
529
- any,
530
- any,
531
- any,
532
- any,
533
- any,
534
- any,
535
- any,
536
- any,
537
- any,
538
- any,
539
- any
540
- >
541
- }) => any
542
- >
543
- emit: {}
544
- }>
545
- ) => import("vue").VNode & {
546
- __ctx?: Awaited<typeof __VLS_setup>
547
- }
548
- Array: <Name extends OmegaFormReturn<From, To, TypeProps>["_keys"]>(
549
- __VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"],
550
- __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>,
551
- __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"],
552
- __VLS_setup?: Promise<{
553
- props:
554
- & __VLS_PrettifyLocal<
555
- & Pick<
556
- & Partial<{}>
557
- & Omit<
558
- {} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps,
559
- never
560
- >,
561
- never
562
- >
563
- & (Omit<OmegaArrayProps<From, To, Name>, "form">)
564
- & {}
565
- >
566
- & import("vue").PublicProps
567
- expose(exposed: import("vue").ShallowUnwrapRef<{}>): void
568
- attrs: any
569
- slots: {
570
- "pre-array"?: (props: {
571
- field: CachedFieldApi<From, To, TypeProps>
572
- state: CachedFieldState<From, To, TypeProps>
573
- }) => any
574
- } & {
575
- default?: (props: {
576
- subField: CachedFieldApi<From, To, TypeProps>
577
- subState: CachedFieldState<From, To, TypeProps>
578
- index: number
579
- field: CachedFieldApi<From, To, TypeProps>
580
- }) => any
581
- } & {
582
- "post-array"?: (props: {
583
- field: CachedFieldApi<From, To, TypeProps>
584
- state: CachedFieldState<From, To, TypeProps>
585
- }) => any
586
- } & {
587
- field?: (props: {
588
- field: CachedFieldApi<From, To, TypeProps>
589
- }) => any
590
- }
591
- emit: {}
592
- }>
593
- ) => import("vue").VNode & {
594
- __ctx?: Awaited<typeof __VLS_setup>
595
- }
596
-
597
- AutoGen: <Name extends OmegaFormReturn<From, To, TypeProps>["_keys"]>(
598
- __VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"],
599
- __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>,
600
- __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"],
601
- __VLS_setup?: Promise<{
602
- props:
603
- & __VLS_PrettifyLocal<
604
- Pick<
605
- & Partial<{}>
606
- & Omit<
607
- {} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps,
608
- never
609
- >,
610
- never
611
- > & {
612
- // form: OmegaInputProps<From, To>["form"]
613
- pick?: OmegaFormReturn<From, To, TypeProps>["_keys"][]
614
- omit?: OmegaFormReturn<From, To, TypeProps>["_keys"][]
615
- labelMap?: (key: OmegaFormReturn<From, To, TypeProps>["_keys"]) => string | undefined
616
- filterMap?: <M extends OmegaAutoGenMeta<From, To, Name>>(
617
- key: OmegaFormReturn<From, To, TypeProps>["_keys"],
618
- meta: M
619
- ) => boolean | M
620
- order?: OmegaFormReturn<From, To, TypeProps>["_keys"][]
621
- sort?: Order.Order<OmegaAutoGenMeta<From, To, Name>>
622
- } & {}
623
- >
624
- & import("vue").PublicProps
625
- expose(exposed: import("vue").ShallowUnwrapRef<{}>): void
626
- attrs: any
627
- slots: {
628
- default(props: {
629
- child: OmegaAutoGenMeta<From, To, Name>
630
- }): void
631
- }
632
- emit: {}
633
- }>
634
- ) => import("vue").VNode & {
635
- __ctx?: Awaited<typeof __VLS_setup>
636
- }
637
-
638
- Form: <K extends keyof OmegaFormState<To, From>>(
639
- __VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"],
640
- __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>,
641
- __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"],
642
- __VLS_setup?: Promise<{
643
- props:
644
- & __VLS_PrettifyLocal<
645
- Pick<
646
- & Partial<{}>
647
- & Omit<
648
- {} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps,
649
- never
650
- >,
651
- never
652
- > & {
653
- // form: OmegaFormReturn<From, To, Props>
654
- disabled?: boolean
655
- subscribe?: K[]
656
- } & {}
657
- >
658
- & import("vue").PublicProps
659
- expose(exposed: import("vue").ShallowUnwrapRef<{}>): void
660
- attrs: any
661
- slots: {
662
- default(props: {
663
- subscribedValues: K[] extends undefined[] ? Record<string, never> : Pick<OmegaFormState<From, To>, K>
664
- }): void
665
- }
666
- emit: {}
667
- }>
668
- ) => import("vue").VNode & {
669
- __ctx?: Awaited<typeof __VLS_setup>
670
- }
671
- }
672
-
673
32
  const runPromise = makeRunPromise(Context.empty())
674
33
 
675
34
  export const useOmegaForm = <
@@ -684,94 +43,36 @@ export const useOmegaForm = <
684
43
  omegaConfig?: OmegaConfig<To>
685
44
  ): OmegaFormReturn<From, To, TypeProps> => {
686
45
  if (!schema) throw new Error("Schema is required")
46
+ const { trans } = useIntl()
687
47
  const formCompatibleSchema = toFormSchema(schema)
688
- const standardSchema = S.toStandardSchemaV1(formCompatibleSchema)
48
+ // Effect's Standard Schema formatter emits `Expected X | Y, got Z` for
49
+ // `AnyOf` issues without consulting our hooks. Pre-annotate literal-union
50
+ // (select) and literal-array (multiple) AST nodes with a localized
51
+ // `message` so the formatter picks them up via `findMessage`.
52
+ const localizedSchema = annotateLiteralUnionMessages(formCompatibleSchema, trans)
53
+ const standardSchema = toLocalizedStandardSchemaV1(
54
+ localizedSchema as any,
55
+ trans
56
+ )
689
57
  const decode = S.decodeUnknownEffect(formCompatibleSchema)
690
58
 
691
59
  const { meta, unionMeta } = generateMetaFromSchema(formCompatibleSchema)
692
60
 
693
- const persistencyKey = computed(() => {
694
- if (omegaConfig?.persistency?.id) {
695
- return omegaConfig.persistency.id
696
- }
697
- const path = window.location.pathname
698
- const keys = Object.keys(meta)
699
- return `${path}-${keys.join("-")}`
700
- })
701
-
702
- const clearUrlParams = () => {
703
- const params = new URLSearchParams(window.location.search)
704
- params.delete(persistencyKey.value)
705
- const url = new URL(window.location.href)
706
- url.search = params.toString()
707
- window.history.replaceState({}, "", url.toString())
708
- }
709
-
710
- const defaultValues = computed(() => {
711
- // will contain what we get from querystring or local/session storage
712
- let persistencyDefaultValues
713
-
714
- const persistency = omegaConfig?.persistency
715
-
716
- if (
717
- // query string has higher priority than local/session storage
718
- persistency?.policies
719
- && !persistencyDefaultValues
720
- && (includesPolicy(persistency.policies, "local")
721
- || includesPolicy(persistency.policies, "session"))
722
- ) {
723
- const storage = includesPolicy(persistency.policies, "local")
724
- ? localStorage
725
- : sessionStorage
726
- if (storage) {
727
- try {
728
- const value = JSON.parse(
729
- storage.getItem(persistencyKey.value) || "{}"
730
- )
731
- storage.removeItem(persistencyKey.value)
732
- persistencyDefaultValues = value
733
- } catch (error) {
734
- console.error(error)
735
- }
736
- }
737
- }
738
- if (persistency?.policies && includesPolicy(persistency.policies, "querystring")) {
739
- try {
740
- const params = new URLSearchParams(window.location.search)
741
- const value = params.get(persistencyKey.value)
742
- clearUrlParams()
743
- if (value) {
744
- persistencyDefaultValues = deepMerge(persistencyDefaultValues || {}, JSON.parse(value))
745
- }
746
- } catch (error) {
747
- console.error(error)
748
- }
749
- }
750
-
751
- // to be sure we have a valid object at the end of the gathering process
752
- persistencyDefaultValues ??= {}
753
-
754
- const defaults: Record<defaultValuesPriorityUnion, any> = {
755
- tanstack: tanstackFormOptions?.defaultValues || {},
756
- persistency: persistencyDefaultValues,
757
- schema: defaultsValueFromSchema(schema)
758
- }
759
-
760
- return (omegaConfig?.defaultValuesPriority || ["tanstack", "persistency", "schema"] as const).reverse().reduce(
761
- (acc, m) => {
762
- if (!Object.keys(acc).length) {
763
- return defaults[m]
764
- }
765
- return deepMerge(acc, defaults[m])
766
- },
767
- {}
768
- )
61
+ // Persistency must be created before `useForm` so its merged
62
+ // `defaultValues` (tanstack + storage/querystring + schema) can flow into
63
+ // the form. The `getForm` accessor is lazy because the form is constructed
64
+ // immediately after, and persistency's listeners only fire later.
65
+ const formHolder: { form: any } = { form: undefined }
66
+ const persistency = usePersistency<From>({
67
+ meta,
68
+ persistency: omegaConfig?.persistency,
69
+ preventWindowExit: omegaConfig?.preventWindowExit,
70
+ defaultValuesPriority: omegaConfig?.defaultValuesPriority,
71
+ tanstackDefaultValues: tanstackFormOptions?.defaultValues,
72
+ schemaDefaultValues: () => defaultsValueFromSchema(schema),
73
+ getForm: () => formHolder.form
769
74
  })
770
75
 
771
- const wrapWithSpan = (span: api.Span | undefined, toWrap: () => any) => {
772
- return span ? api.context.with(api.trace.setSpan(api.context.active(), span), toWrap) : toWrap()
773
- }
774
-
775
76
  const form = useForm<
776
77
  From,
777
78
  FormValidateOrFn<From> | undefined,
@@ -787,34 +88,16 @@ export const useOmegaForm = <
787
88
  Record<string, any> | undefined
788
89
  >({
789
90
  ...tanstackFormOptions,
91
+ validationLogic: revalidateLogic(),
790
92
  validators: {
791
- onSubmit: standardSchema,
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ onDynamic: standardSchema as any,
792
95
  ...tanstackFormOptions?.validators
793
96
  },
794
- onSubmit: tanstackFormOptions?.onSubmit
795
- ? ({ formApi, meta, value }) =>
796
- wrapWithSpan(meta?.currentSpan, async () => {
797
- // validators only validate, they don't actually transform, so we have to do that manually here.
798
- const parsedValue = await runPromise(decode(value))
799
- const r = tanstackFormOptions.onSubmit!({
800
- formApi: formApi as OmegaFormApi<From, To>,
801
- meta,
802
- value: parsedValue
803
- })
804
- if (Fiber.isFiber(r)) {
805
- return await runtimeFiberAsPromise(r)
806
- }
807
- if (Effect.isEffect(r)) {
808
- const effectResult = await runPromise(r)
809
- return Fiber.isFiber(effectResult)
810
- ? await runtimeFiberAsPromise(effectResult)
811
- : effectResult
812
- }
813
- return r
814
- })
815
- : undefined,
816
- defaultValues: defaultValues.value as any
97
+ onSubmit: wrapOnSubmit<From, To>(tanstackFormOptions?.onSubmit, decode, runPromise),
98
+ defaultValues: persistency.defaultValues.value as any
817
99
  }) satisfies OmegaFormApi<To, From>
100
+ formHolder.form = form
818
101
 
819
102
  const clear = () => {
820
103
  Object.keys(meta).forEach((key: any) => {
@@ -822,89 +105,6 @@ export const useOmegaForm = <
822
105
  })
823
106
  }
824
107
 
825
- const createNestedObjectFromPaths = (paths: string[]) =>
826
- paths.reduce((result, path) => {
827
- const parts = path.split(".")
828
- parts.reduce((acc, part, i) => {
829
- if (i === parts.length - 1) {
830
- acc[part] = form.getFieldValue(path as any)
831
- } else {
832
- acc[part] = acc[part] ?? {}
833
- }
834
- return acc[part]
835
- }, result)
836
- return result
837
- }, {} as Record<string, any>)
838
-
839
- const persistFilter = (persistency: OmegaConfig<From>["persistency"]) => {
840
- if (!persistency) return
841
- const { banKeys, keys } = persistency
842
- if (Array.isArray(keys)) {
843
- return createNestedObjectFromPaths(keys as string[])
844
- }
845
- if (Array.isArray(banKeys)) {
846
- const subs = Object.keys(meta).filter((metakey) => banKeys.includes(metakey as any))
847
- return createNestedObjectFromPaths(subs)
848
- }
849
- return form.store.state.values
850
- }
851
-
852
- const persistData = () => {
853
- const persistency = omegaConfig?.persistency
854
- if (!persistency?.policies || persistency.policies.length === 0) {
855
- return
856
- }
857
- if (
858
- includesPolicy(persistency.policies, "local")
859
- || includesPolicy(persistency.policies, "session")
860
- ) {
861
- const storage = includesPolicy(persistency.policies, "local")
862
- ? localStorage
863
- : sessionStorage
864
- if (!storage) return
865
- const values = persistFilter(persistency)
866
- return storage.setItem(persistencyKey.value, JSON.stringify(values))
867
- }
868
- }
869
-
870
- const saveDataInUrl = () => {
871
- const persistency = omegaConfig?.persistency
872
- if (!persistency?.policies || persistency.policies.length === 0) {
873
- return
874
- }
875
- if (includesPolicy(persistency.policies, "querystring")) {
876
- const values = persistFilter(persistency)
877
- const searchParams = new URLSearchParams(window.location.search)
878
- searchParams.set(persistencyKey.value, JSON.stringify(values))
879
- const url = new URL(window.location.href)
880
- url.search = searchParams.toString()
881
- window.history.replaceState({}, "", url.toString())
882
- }
883
- }
884
-
885
- const preventWindowExit = (e: BeforeUnloadEvent) => {
886
- if (form.store.state.isDirty) {
887
- e.preventDefault()
888
- }
889
- }
890
-
891
- onUnmounted(persistData)
892
-
893
- onMounted(() => {
894
- window.addEventListener("beforeunload", persistData)
895
- window.addEventListener("blur", saveDataInUrl)
896
- if (omegaConfig?.preventWindowExit && omegaConfig.preventWindowExit !== "nope") {
897
- window.addEventListener("beforeunload", preventWindowExit)
898
- }
899
- })
900
- onBeforeUnmount(() => {
901
- window.removeEventListener("beforeunload", persistData)
902
- window.removeEventListener("blur", saveDataInUrl)
903
- if (omegaConfig?.preventWindowExit && omegaConfig.preventWindowExit !== "nope") {
904
- window.removeEventListener("beforeunload", preventWindowExit)
905
- }
906
- })
907
-
908
108
  // Watch for successful form submissions and auto-reset if prevent-and-reset is enabled
909
109
  // We put it as a side effect, so we don't overwhelm submit handler and we can support
910
110
  // effects submission more freely
@@ -923,39 +123,9 @@ export const useOmegaForm = <
923
123
  })
924
124
  }
925
125
 
926
- const hs = form.handleSubmit
126
+ const { handleSubmit, handleSubmitEffect } = makeSubmitHandlers<From, To>(form)
927
127
 
928
- const handleSubmit: typeof form.handleSubmit = async (meta?: Record<string, any>) => {
929
- // workaround for not revealing all form errors on submit
930
- // await form.validateAllFields("blur")
931
- return await hs(meta)
932
- }
933
-
934
- const handleSubmitEffect_ = (meta?: Record<string, any>) =>
935
- Effect.currentSpan.pipe(
936
- Effect.option,
937
- Effect
938
- .flatMap((span) =>
939
- Effect.promise(() => handleSubmit(Option.isSome(span) ? { currentSpan: span.value, ...meta } : meta))
940
- )
941
- )
942
-
943
- const handleSubmitEffect: {
944
- (options: { checkErrors: true; meta?: Record<string, any> }): Effect.Effect<void, FormErrors<From>>
945
- (options?: { meta?: Record<string, any> }): Effect.Effect<void>
946
- } = (
947
- options?: { meta?: Record<string, any>; checkErrors?: true }
948
- ): any =>
949
- options?.checkErrors
950
- ? handleSubmitEffect_(options?.meta).pipe(Effect.flatMap(Effect.fnUntraced(function*() {
951
- const errors = form.getAllErrors()
952
- if (Object.keys(errors.fields).length || errors.form.errors.length) {
953
- return yield* Effect.fail(new FormErrors({ form: errors.form, fields: errors.fields }))
954
- }
955
- })))
956
- : handleSubmitEffect_(options?.meta)
957
-
958
- const fieldMap = ref(new Map<string, { label: string; id: string }>())
128
+ const { fieldMap, registerField } = makeFieldMap()
959
129
 
960
130
  const formWithExtras: OF<From, To> = Object.assign(form, {
961
131
  i18nNamespace: omegaConfig?.i18nNamespace,
@@ -963,46 +133,12 @@ export const useOmegaForm = <
963
133
  meta,
964
134
  unionMeta,
965
135
  clear,
966
- handleSubmit: (meta?: Record<string, any>) => {
967
- const span = api.trace.getSpan(api.context.active())
968
- return handleSubmit({ currentSpan: span, ...meta })
969
- },
136
+ handleSubmit,
970
137
  // /** @experimental */
971
138
  handleSubmitEffect,
972
- registerField: (field: ComputedRef<{ name: string; label: string; id: string }>) => {
973
- watch(field, (f) => {
974
- fieldMap.value.set(f.name, { label: f.label, id: f.id })
975
- }, { immediate: true })
976
- onUnmounted(() => {
977
- // Only delete if we still own this entry (id matches)
978
- // This prevents old components from deleting entries registered by new components
979
- // during re-mount transitions (e.g., when :key changes)
980
- const currentEntry = fieldMap.value.get(field.value.name)
981
- if (currentEntry?.id === field.value.id) {
982
- fieldMap.value.delete(field.value.name)
983
- }
984
- })
985
- }
139
+ registerField
986
140
  })
987
141
 
988
- // Clear all field onSubmit errors when any value changes after a failed submission.
989
- // Form-level onSubmit validation (e.g. union schemas) distributes errors to individual fields.
990
- // TanStack only clears the changed field's onSubmit error, leaving sibling fields with stale
991
- // errors that keep isFieldsValid=false and block re-submission.
992
- const lastSubmitAttempts = ref(0)
993
- const submissionAttempts = form.useStore((s) => s.submissionAttempts)
994
- const formValues = form.useStore((s) => s.values)
995
- watch(formValues, () => {
996
- if (lastSubmitAttempts.value === submissionAttempts.value) return
997
- lastSubmitAttempts.value = submissionAttempts.value
998
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
999
- for (const info of Object.values(form.fieldInfo) as any[]) {
1000
- if (info?.instance?.state.meta.errorMap?.onSubmit) {
1001
- info.instance.setMeta((prev: any) => ({ ...prev, errorMap: { ...prev.errorMap, onSubmit: undefined } }))
1002
- }
1003
- }
1004
- }, { deep: true })
1005
-
1006
142
  const errorContext = { form: formWithExtras, fieldMap }
1007
143
 
1008
144
  return Object.assign(formWithExtras, {