@effect-app/vue-components 4.0.0-beta.21 → 4.0.0-beta.210

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