@effect-app/vue-components 4.0.0-beta.22 → 4.0.0-beta.221

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 (264) hide show
  1. package/README.md +13 -9
  2. package/dist/reset.css +39 -38
  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 +2 -2
  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 +33 -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 +283 -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 +8 -8
  30. package/dist/vue-components.es.js +29 -44
  31. package/dist/vue-components10.es.js +5 -0
  32. package/dist/vue-components100.es.js +269 -0
  33. package/dist/vue-components102.es.js +8 -0
  34. package/dist/vue-components103.es.js +73 -0
  35. package/dist/vue-components104.es.js +5 -0
  36. package/dist/vue-components105.es.js +52 -0
  37. package/dist/vue-components106.es.js +5 -0
  38. package/dist/vue-components107.es.js +24 -0
  39. package/dist/vue-components108.es.js +5 -0
  40. package/dist/vue-components109.es.js +59 -0
  41. package/dist/vue-components11.es.js +20 -0
  42. package/dist/vue-components110.es.js +5 -0
  43. package/dist/vue-components111.es.js +12 -0
  44. package/dist/vue-components112.es.js +22 -0
  45. package/dist/vue-components114.es.js +9 -0
  46. package/dist/vue-components115.es.js +4 -0
  47. package/dist/vue-components116.es.js +38 -0
  48. package/dist/vue-components117.es.js +27 -0
  49. package/dist/vue-components118.es.js +28 -0
  50. package/dist/vue-components119.es.js +7 -0
  51. package/dist/vue-components12.es.js +27 -0
  52. package/dist/vue-components120.es.js +18 -0
  53. package/dist/vue-components121.es.js +36 -0
  54. package/dist/vue-components122.es.js +18 -0
  55. package/dist/vue-components123.es.js +21 -0
  56. package/dist/vue-components124.es.js +30 -0
  57. package/dist/vue-components125.es.js +7 -0
  58. package/dist/vue-components126.es.js +9 -0
  59. package/dist/vue-components127.es.js +38 -0
  60. package/dist/vue-components128.es.js +25 -0
  61. package/dist/vue-components129.es.js +128 -0
  62. package/dist/vue-components13.es.js +70 -0
  63. package/dist/vue-components130.es.js +24 -0
  64. package/dist/vue-components131.es.js +21 -0
  65. package/dist/vue-components132.es.js +9 -0
  66. package/dist/vue-components133.es.js +19 -0
  67. package/dist/vue-components134.es.js +5 -0
  68. package/dist/vue-components135.es.js +29 -0
  69. package/dist/vue-components136.es.js +5 -0
  70. package/dist/vue-components137.es.js +43 -0
  71. package/dist/vue-components138.es.js +82 -0
  72. package/dist/vue-components139.es.js +33 -0
  73. package/dist/vue-components14.es.js +15 -0
  74. package/dist/vue-components140.es.js +19 -0
  75. package/dist/vue-components141.es.js +48 -0
  76. package/dist/vue-components15.es.js +29 -0
  77. package/dist/vue-components16.es.js +53 -0
  78. package/dist/vue-components17.es.js +87 -0
  79. package/dist/vue-components18.es.js +4 -0
  80. package/dist/vue-components19.es.js +4 -0
  81. package/dist/vue-components2.es.js +20 -0
  82. package/dist/vue-components20.es.js +19 -0
  83. package/dist/vue-components21.es.js +73 -0
  84. package/dist/vue-components22.es.js +14 -0
  85. package/dist/vue-components23.es.js +26 -0
  86. package/dist/vue-components24.es.js +252 -0
  87. package/dist/vue-components25.es.js +65 -0
  88. package/dist/vue-components26.es.js +68 -0
  89. package/dist/vue-components27.es.js +8 -0
  90. package/dist/vue-components28.es.js +12 -0
  91. package/dist/vue-components29.es.js +6 -0
  92. package/dist/vue-components3.es.js +86 -0
  93. package/dist/vue-components30.es.js +28 -0
  94. package/dist/vue-components31.es.js +75 -0
  95. package/dist/vue-components32.es.js +109 -0
  96. package/dist/vue-components33.es.js +57 -0
  97. package/dist/vue-components34.es.js +7 -0
  98. package/dist/vue-components35.es.js +4 -0
  99. package/dist/vue-components36.es.js +6 -0
  100. package/dist/vue-components37.es.js +688 -0
  101. package/dist/vue-components38.es.js +7 -0
  102. package/dist/vue-components39.es.js +5 -0
  103. package/dist/vue-components4.es.js +5 -0
  104. package/dist/vue-components40.es.js +6 -0
  105. package/dist/vue-components41.es.js +27 -0
  106. package/dist/vue-components42.es.js +5 -0
  107. package/dist/vue-components43.es.js +48 -0
  108. package/dist/vue-components44.es.js +70 -0
  109. package/dist/vue-components45.es.js +9 -0
  110. package/dist/vue-components46.es.js +10 -0
  111. package/dist/vue-components47.es.js +8 -0
  112. package/dist/vue-components48.es.js +173 -0
  113. package/dist/vue-components49.es.js +14 -0
  114. package/dist/vue-components5.es.js +24 -0
  115. package/dist/vue-components50.es.js +11 -0
  116. package/dist/vue-components51.es.js +4 -0
  117. package/dist/vue-components52.es.js +7 -0
  118. package/dist/vue-components53.es.js +29 -0
  119. package/dist/vue-components54.es.js +221 -0
  120. package/dist/vue-components55.es.js +85 -0
  121. package/dist/vue-components56.es.js +74 -0
  122. package/dist/vue-components57.es.js +819 -0
  123. package/dist/vue-components58.es.js +4 -0
  124. package/dist/vue-components59.es.js +109 -0
  125. package/dist/vue-components6.es.js +13 -0
  126. package/dist/vue-components60.es.js +54 -0
  127. package/dist/vue-components61.es.js +520 -0
  128. package/dist/vue-components62.es.js +440 -0
  129. package/dist/vue-components63.es.js +10 -0
  130. package/dist/vue-components64.es.js +13 -0
  131. package/dist/vue-components65.es.js +19 -0
  132. package/dist/vue-components66.es.js +29 -0
  133. package/dist/vue-components67.es.js +22 -0
  134. package/dist/vue-components68.es.js +30 -0
  135. package/dist/vue-components69.es.js +29 -0
  136. package/dist/vue-components7.es.js +13 -0
  137. package/dist/vue-components70.es.js +73 -0
  138. package/dist/vue-components71.es.js +6 -0
  139. package/dist/vue-components72.es.js +18 -0
  140. package/dist/vue-components73.es.js +5 -0
  141. package/dist/vue-components74.es.js +70 -0
  142. package/dist/vue-components75.es.js +140 -0
  143. package/dist/vue-components76.es.js +4 -0
  144. package/dist/vue-components77.es.js +21 -0
  145. package/dist/vue-components78.es.js +55 -0
  146. package/dist/vue-components79.es.js +9 -0
  147. package/dist/vue-components8.es.js +35 -0
  148. package/dist/vue-components80.es.js +16 -0
  149. package/dist/vue-components81.es.js +39 -0
  150. package/dist/vue-components82.es.js +49 -0
  151. package/dist/vue-components83.es.js +128 -0
  152. package/dist/vue-components84.es.js +65 -0
  153. package/dist/vue-components85.es.js +63 -0
  154. package/dist/vue-components86.es.js +25 -0
  155. package/dist/vue-components87.es.js +5 -0
  156. package/dist/vue-components88.es.js +80 -0
  157. package/dist/vue-components89.es.js +95 -0
  158. package/dist/vue-components9.es.js +47 -0
  159. package/dist/vue-components90.es.js +73 -0
  160. package/dist/vue-components91.es.js +12 -0
  161. package/dist/vue-components92.es.js +56 -0
  162. package/dist/vue-components93.es.js +5 -0
  163. package/dist/vue-components94.es.js +44 -0
  164. package/dist/vue-components95.es.js +5 -0
  165. package/dist/vue-components96.es.js +84 -0
  166. package/dist/vue-components98.es.js +8 -0
  167. package/dist/vue-components99.es.js +9 -0
  168. package/package.json +29 -29
  169. package/src/components/CommandButton.vue +55 -7
  170. package/src/components/OmegaForm/OmegaArray.vue +2 -4
  171. package/src/components/OmegaForm/OmegaAutoGen.vue +3 -2
  172. package/src/components/OmegaForm/OmegaErrorsInternal.vue +1 -1
  173. package/src/components/OmegaForm/OmegaFormInput.vue +1 -1
  174. package/src/components/OmegaForm/OmegaInput.vue +7 -36
  175. package/src/components/OmegaForm/OmegaInputVuetify.vue +5 -2
  176. package/src/components/OmegaForm/OmegaInternalInput.vue +12 -6
  177. package/src/components/OmegaForm/OmegaTaggedUnion.vue +2 -1
  178. package/src/components/OmegaForm/OmegaTaggedUnionInternal.vue +1 -1
  179. package/src/components/OmegaForm/OmegaWrapper.vue +1 -1
  180. package/src/components/OmegaForm/blockDialog.ts +18 -6
  181. package/src/components/OmegaForm/createUseFormWithCustomInput.ts +2 -1
  182. package/src/components/OmegaForm/errors.ts +136 -0
  183. package/src/components/OmegaForm/getOmegaStore.ts +1 -1
  184. package/src/components/OmegaForm/hocs.ts +19 -0
  185. package/src/components/OmegaForm/index.ts +16 -4
  186. package/src/components/OmegaForm/inputs.ts +22 -0
  187. package/src/components/OmegaForm/meta/checks.ts +82 -0
  188. package/src/components/OmegaForm/meta/createMeta.ts +140 -0
  189. package/src/components/OmegaForm/meta/defaults.ts +134 -0
  190. package/src/components/OmegaForm/meta/redacted.ts +66 -0
  191. package/src/components/OmegaForm/meta/types.ts +78 -0
  192. package/src/components/OmegaForm/meta/walker.ts +248 -0
  193. package/src/components/OmegaForm/persistency.ts +247 -0
  194. package/src/components/OmegaForm/submit.ts +131 -0
  195. package/src/components/OmegaForm/types.ts +753 -0
  196. package/src/components/OmegaForm/useOmegaForm.ts +59 -893
  197. package/src/components/OmegaForm/useRegisterField.ts +1 -1
  198. package/src/components/OmegaForm/validation/localized.ts +203 -0
  199. package/src/index.ts +0 -1
  200. package/src/reset.css +39 -38
  201. package/src/utils/index.ts +11 -8
  202. package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +0 -159
  203. package/dist/types/constants/index.d.ts +0 -1
  204. package/dist/vue-components.es10.js +0 -239
  205. package/dist/vue-components.es11.js +0 -32
  206. package/dist/vue-components.es12.js +0 -503
  207. package/dist/vue-components.es13.js +0 -49
  208. package/dist/vue-components.es14.js +0 -4
  209. package/dist/vue-components.es15.js +0 -4
  210. package/dist/vue-components.es16.js +0 -6
  211. package/dist/vue-components.es17.js +0 -13
  212. package/dist/vue-components.es18.js +0 -57
  213. package/dist/vue-components.es19.js +0 -56
  214. package/dist/vue-components.es2.js +0 -30
  215. package/dist/vue-components.es20.js +0 -8
  216. package/dist/vue-components.es21.js +0 -8
  217. package/dist/vue-components.es22.js +0 -5
  218. package/dist/vue-components.es23.js +0 -5
  219. package/dist/vue-components.es24.js +0 -4
  220. package/dist/vue-components.es25.js +0 -4
  221. package/dist/vue-components.es26.js +0 -4
  222. package/dist/vue-components.es27.js +0 -4
  223. package/dist/vue-components.es28.js +0 -19
  224. package/dist/vue-components.es29.js +0 -13
  225. package/dist/vue-components.es3.js +0 -17
  226. package/dist/vue-components.es30.js +0 -194
  227. package/dist/vue-components.es32.js +0 -31
  228. package/dist/vue-components.es33.js +0 -6
  229. package/dist/vue-components.es34.js +0 -4
  230. package/dist/vue-components.es35.js +0 -4
  231. package/dist/vue-components.es36.js +0 -113
  232. package/dist/vue-components.es38.js +0 -9
  233. package/dist/vue-components.es39.js +0 -34
  234. package/dist/vue-components.es4.js +0 -52
  235. package/dist/vue-components.es41.js +0 -6
  236. package/dist/vue-components.es42.js +0 -25
  237. package/dist/vue-components.es43.js +0 -7
  238. package/dist/vue-components.es44.js +0 -23
  239. package/dist/vue-components.es45.js +0 -32
  240. package/dist/vue-components.es46.js +0 -24
  241. package/dist/vue-components.es47.js +0 -14
  242. package/dist/vue-components.es48.js +0 -7
  243. package/dist/vue-components.es49.js +0 -21
  244. package/dist/vue-components.es5.js +0 -52
  245. package/dist/vue-components.es50.js +0 -11
  246. package/dist/vue-components.es51.js +0 -33
  247. package/dist/vue-components.es52.js +0 -50
  248. package/dist/vue-components.es53.js +0 -28
  249. package/dist/vue-components.es54.js +0 -13
  250. package/dist/vue-components.es55.js +0 -67
  251. package/dist/vue-components.es56.js +0 -58
  252. package/dist/vue-components.es57.js +0 -19
  253. package/dist/vue-components.es58.js +0 -35
  254. package/dist/vue-components.es59.js +0 -31
  255. package/dist/vue-components.es6.js +0 -69
  256. package/dist/vue-components.es60.js +0 -44
  257. package/dist/vue-components.es61.js +0 -4
  258. package/dist/vue-components.es62.js +0 -46
  259. package/dist/vue-components.es63.js +0 -4
  260. package/dist/vue-components.es7.js +0 -83
  261. package/dist/vue-components.es8.js +0 -63
  262. package/dist/vue-components.es9.js +0 -21
  263. package/src/components/OmegaForm/OmegaFormStuff.ts +0 -1276
  264. package/src/constants/index.ts +0 -1
@@ -1,1276 +0,0 @@
1
- import { Effect, Option, type Record, S } from "effect-app"
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
- import { type DeepKeys, type DeepValue, type FieldAsyncValidateOrFn, type FieldValidateOrFn, type FormApi, type FormAsyncValidateOrFn, type FormOptions, type FormState, type FormValidateOrFn, type StandardSchemaV1, type VueFormApi } from "@tanstack/vue-form"
4
- import { isObject } from "@vueuse/core"
5
- import type { Fiber as EffectFiber } from "effect/Fiber"
6
- import { getTransformationFrom, useIntl } from "../../utils"
7
- import { type OmegaFieldInternalApi } from "./InputProps"
8
- import { type OF, type OmegaFormReturn } from "./useOmegaForm"
9
-
10
- const legacyTagWarningEmittedFor = new Set<string>()
11
- type GlobalThisWithOptionalProcess = typeof globalThis & {
12
- process?: {
13
- env?: {
14
- NODE_ENV?: string
15
- }
16
- }
17
- }
18
-
19
- const isDevelopmentEnvironment = () => {
20
- const process = (globalThis as GlobalThisWithOptionalProcess).process
21
- return process?.env?.NODE_ENV !== "production"
22
- }
23
-
24
- export type FieldPath<T> = unknown extends T ? string
25
- // technically we cannot have primitive at the root
26
- : T extends string | boolean | number | null | undefined | symbol | bigint ? ""
27
- // technically we cannot have array at the root
28
- : T extends ReadonlyArray<infer U> ? FieldPath_<U, `[${number}]`>
29
- : {
30
- [K in keyof T]: FieldPath_<T[K], `${K & string}`>
31
- }[keyof T]
32
-
33
- export type FieldPath_<T, Path extends string> = unknown extends T ? string
34
- : T extends string | boolean | number | null | undefined | symbol | bigint ? Path
35
- : T extends ReadonlyArray<infer U> ? FieldPath_<U, `${Path}[${number}]`> | Path
36
- : {
37
- [K in keyof T]: FieldPath_<T[K], `${Path}.${K & string}`>
38
- }[keyof T]
39
-
40
- export type BaseProps<From, TName extends FieldPath<From>> = {
41
- /**
42
- * Will fallback to i18n when not specified.
43
- * Can also be provided via #label slot for custom HTML labels.
44
- * When using the slot, it receives bindings: { required, id, label }
45
- */
46
- label?: string
47
- validators?: FieldValidators<From>
48
- // Use FlexibleArrayPath: if name contains [], just use TName; otherwise intersect with Leaves<From>
49
- name: TName
50
- /**
51
- * Optional class to apply to the input element.
52
- * - If a string is provided, it will be used instead of the general class
53
- * - If null is provided, no class will be applied (neither inputClass nor general class)
54
- * - If undefined (not provided), the general class will be used
55
- */
56
- inputClass?: string | null
57
- }
58
-
59
- export type TypesWithOptions = "radio" | "select" | "multiple" | "autocomplete" | "autocompletemultiple"
60
- export type DefaultTypeProps = {
61
- type?: TypeOverride
62
- options?: undefined
63
- } | {
64
- type?: TypesWithOptions
65
- // TODO: options should depend on `type`, but since there is auto-type, we can't currently enforce it.
66
- // hence we allow it also for type? (undefined) atm
67
- options?: {
68
- title: string
69
- value: unknown
70
- }[]
71
- }
72
-
73
- export type OmegaInputPropsBase<
74
- From extends Record<PropertyKey, any>,
75
- To extends Record<PropertyKey, any>,
76
- Name extends DeepKeys<From>
77
- > = {
78
- form: OF<From, To> & {
79
- meta: MetaRecord<From>
80
- i18nNamespace?: string
81
- }
82
- } & BaseProps<From, Name>
83
-
84
- export type OmegaInputProps<
85
- From extends Record<PropertyKey, any>,
86
- To extends Record<PropertyKey, any>,
87
- Name extends DeepKeys<From>,
88
- TypeProps = DefaultTypeProps
89
- > = {
90
- form: OmegaFormReturn<From, To, TypeProps> & {
91
- meta: MetaRecord<From>
92
- i18nNamespace?: string
93
- }
94
- } & BaseProps<From, Name>
95
-
96
- export type OmegaArrayProps<
97
- From extends Record<PropertyKey, any>,
98
- To extends Record<PropertyKey, any>,
99
- Name extends DeepKeys<From>
100
- > =
101
- & Omit<
102
- OmegaInputProps<From, To, Name>,
103
- "validators" | "options" | "label" | "type" | "items" | "name"
104
- >
105
- & {
106
- name: DeepKeys<From>
107
- defaultItems?: DeepValue<From, DeepKeys<From>>
108
- // deprecated items, caused bugs in state update, use defaultItems instead. It's not a simple Never, because Volar explodes
109
- items?: "please use `defaultItems` instead"
110
- }
111
-
112
- export type TypeOverride =
113
- | "string"
114
- | "text"
115
- | "number"
116
- | "select"
117
- | "multiple"
118
- | "boolean"
119
- | "radio"
120
- | "autocomplete"
121
- | "autocompletemultiple"
122
- | "switch"
123
- | "range"
124
- | "password"
125
- | "email"
126
- | "date"
127
-
128
- export interface OmegaError {
129
- label: string
130
- inputId: string
131
- errors: readonly string[]
132
- }
133
-
134
- export type FormProps<From, To> =
135
- & Omit<
136
- FormOptions<
137
- From,
138
- FormValidateOrFn<From> | undefined,
139
- FormValidateOrFn<From> | undefined,
140
- StandardSchemaV1<From, To>,
141
- FormValidateOrFn<From> | undefined,
142
- FormAsyncValidateOrFn<From> | undefined,
143
- FormValidateOrFn<From> | undefined,
144
- FormAsyncValidateOrFn<From> | undefined,
145
- FormValidateOrFn<From> | undefined,
146
- FormAsyncValidateOrFn<From> | undefined,
147
- FormAsyncValidateOrFn<From> | undefined,
148
- Record<string, any> | undefined // TODO
149
- >,
150
- | "onSubmit"
151
- | "defaultValues"
152
- >
153
- & {
154
- // when defaultValues are allowed to be undefined, then they should also be allowed to be partial
155
- // this fixes validator issues where a defaultValue of "" leads to "requires at least 1 character", while manually emptying the field changes it to "is required"
156
- defaultValues?: Partial<From>
157
- onSubmit?: (props: {
158
- formApi: OmegaFormParams<From, To>
159
- meta: any
160
- value: To
161
- }) => Promise<any> | EffectFiber<any, any> | Effect.Effect<unknown, any, never>
162
- }
163
-
164
- export type OmegaFormParams<From, To> = FormApi<
165
- From,
166
- FormValidateOrFn<From> | undefined,
167
- FormValidateOrFn<From> | undefined,
168
- StandardSchemaV1<From, To>,
169
- FormValidateOrFn<From> | undefined,
170
- FormAsyncValidateOrFn<From> | undefined,
171
- FormValidateOrFn<From> | undefined,
172
- FormAsyncValidateOrFn<From> | undefined,
173
- FormValidateOrFn<From> | undefined,
174
- FormAsyncValidateOrFn<From> | undefined,
175
- FormAsyncValidateOrFn<From> | undefined,
176
- Record<string, any> | undefined
177
- >
178
-
179
- export type OmegaFormState<From, To> = FormState<
180
- From,
181
- FormValidateOrFn<From> | undefined,
182
- FormValidateOrFn<From> | undefined,
183
- StandardSchemaV1<From, To>,
184
- FormValidateOrFn<From> | undefined,
185
- FormAsyncValidateOrFn<From> | undefined,
186
- FormValidateOrFn<From> | undefined,
187
- FormAsyncValidateOrFn<From> | undefined,
188
- FormValidateOrFn<From> | undefined,
189
- FormAsyncValidateOrFn<From> | undefined,
190
- FormAsyncValidateOrFn<From> | undefined
191
- >
192
-
193
- // TODO: stitch TSubmitMeta somehow
194
- export type OmegaFormApi<From, To, TSubmitMeta = Record<string, any> | undefined> =
195
- & OmegaFormParams<From, To>
196
- & VueFormApi<
197
- From,
198
- FormValidateOrFn<From> | undefined,
199
- FormValidateOrFn<From> | undefined,
200
- StandardSchemaV1<From, To>,
201
- FormValidateOrFn<From> | undefined,
202
- FormAsyncValidateOrFn<From> | undefined,
203
- FormValidateOrFn<From> | undefined,
204
- FormAsyncValidateOrFn<From> | undefined,
205
- FormValidateOrFn<From> | undefined,
206
- FormAsyncValidateOrFn<From> | undefined,
207
- FormAsyncValidateOrFn<From> | undefined,
208
- TSubmitMeta
209
- >
210
-
211
- export type FormComponent<T, S> = VueFormApi<
212
- T,
213
- FormValidateOrFn<T> | undefined,
214
- FormValidateOrFn<T> | undefined,
215
- StandardSchemaV1<T, S>,
216
- FormValidateOrFn<T> | undefined,
217
- FormAsyncValidateOrFn<T> | undefined,
218
- FormValidateOrFn<T> | undefined,
219
- FormAsyncValidateOrFn<T> | undefined,
220
- FormValidateOrFn<T> | undefined,
221
- FormAsyncValidateOrFn<T> | undefined,
222
- FormAsyncValidateOrFn<T> | undefined,
223
- Record<string, any> | undefined
224
- >
225
-
226
- export type FormType<
227
- From extends Record<PropertyKey, any>,
228
- To extends Record<PropertyKey, any>,
229
- Name extends DeepKeys<From>
230
- > = OmegaFormApi<From, To> & {
231
- Field: OmegaFieldInternalApi<From, Name>
232
- }
233
-
234
- export type PrefixFromDepth<
235
- K extends string | number,
236
- _TDepth extends any[]
237
- > = K
238
-
239
- export type NestedKeyOf<T> = DeepKeys<T>
240
-
241
- export type FieldValidators<T> = {
242
- onChangeAsync?: FieldAsyncValidateOrFn<T, any, any>
243
- onChange?: FieldValidateOrFn<T, any, any>
244
- onBlur?: FieldValidateOrFn<T, any, any>
245
- onBlurAsync?: FieldAsyncValidateOrFn<T, any, any>
246
- }
247
-
248
- // Field metadata type definitions
249
- export type BaseFieldMeta = {
250
- required: boolean
251
- nullableOrUndefined?: false | "undefined" | "null"
252
- }
253
-
254
- export type StringFieldMeta = BaseFieldMeta & {
255
- type: "string"
256
- maxLength?: number
257
- minLength?: number
258
- format?: string
259
- }
260
-
261
- export type NumberFieldMeta = BaseFieldMeta & {
262
- type: "number"
263
- minimum?: number
264
- maximum?: number
265
- exclusiveMinimum?: number
266
- exclusiveMaximum?: number
267
- refinement?: "int"
268
- }
269
-
270
- export type SelectFieldMeta = BaseFieldMeta & {
271
- type: "select"
272
- members: any[] // TODO: should be non empty array?
273
- }
274
-
275
- export type MultipleFieldMeta = BaseFieldMeta & {
276
- type: "multiple"
277
- members: any[] // TODO: should be non empty array?
278
- rest: readonly S.AST.AST[]
279
- }
280
-
281
- export type BooleanFieldMeta = BaseFieldMeta & {
282
- type: "boolean"
283
- }
284
-
285
- export type DateFieldMeta = BaseFieldMeta & {
286
- type: "date"
287
- }
288
-
289
- export type UnknownFieldMeta = BaseFieldMeta & {
290
- type: "unknown"
291
- }
292
-
293
- export type FieldMeta =
294
- | StringFieldMeta
295
- | NumberFieldMeta
296
- | SelectFieldMeta
297
- | MultipleFieldMeta
298
- | BooleanFieldMeta
299
- | DateFieldMeta
300
- | UnknownFieldMeta
301
-
302
- export type MetaRecord<T = string> = {
303
- [K in NestedKeyOf<T>]?: FieldMeta
304
- }
305
-
306
- export type FilterItems = {
307
- items: readonly [string, ...string[]]
308
- message:
309
- | string
310
- | Effect.Effect<string, never, never>
311
- | { readonly message: string | Effect.Effect<string> }
312
- }
313
-
314
- export type CreateMeta =
315
- & {
316
- parent?: string
317
- meta?: Record<string, any>
318
- nullableOrUndefined?: false | "undefined" | "null"
319
- }
320
- & (
321
- | {
322
- propertySignatures: readonly S.AST.PropertySignature[]
323
- property?: never
324
- }
325
- | {
326
- propertySignatures?: never
327
- property: S.AST.AST
328
- }
329
- )
330
-
331
- const unwrapDeclaration = (property: S.AST.AST): S.AST.AST => {
332
- let current = getTransformationFrom(property)
333
-
334
- while (S.AST.isDeclaration(current) && current.typeParameters.length > 0) {
335
- current = getTransformationFrom(current.typeParameters[0]!)
336
- }
337
-
338
- return current
339
- }
340
-
341
- const isNullishType = (property: S.AST.AST) => S.AST.isUndefined(property) || S.AST.isNull(property)
342
-
343
- /**
344
- * Unwrap a single-element Union to its inner type if it's a Literal.
345
- * After AST.toType, S.Struct({ _tag: S.Literal("X") }) produces Union([Literal("X")])
346
- * instead of bare Literal("X") like S.TaggedStruct does.
347
- * TODO: remove after manual _tag deprecation
348
- */
349
- const unwrapSingleLiteralUnion = (ast: S.AST.AST): S.AST.AST =>
350
- S.AST.isUnion(ast) && ast.types.length === 1 && S.AST.isLiteral(ast.types[0]!)
351
- ? ast.types[0]!
352
- : ast
353
-
354
- const getNullableOrUndefined = (property: S.AST.AST) =>
355
- S.AST.isUnion(property)
356
- ? property.types.find((_) => isNullishType(_))
357
- : false
358
-
359
- export const isNullableOrUndefined = (property: false | S.AST.AST | undefined) => {
360
- if (!property || !S.AST.isUnion(property)) return false
361
- if (property.types.find((_) => S.AST.isUndefined(_))) {
362
- return "undefined"
363
- }
364
- if (property.types.find((_) => S.AST.isNull(_))) return "null"
365
- return false
366
- }
367
-
368
- // Helper function to recursively unwrap nested unions (e.g., S.NullOr(S.NullOr(X)) -> X)
369
- const unwrapNestedUnions = (types: readonly S.AST.AST[]): readonly S.AST.AST[] => {
370
- const result: S.AST.AST[] = []
371
- for (const type of types) {
372
- if (S.AST.isUnion(type)) {
373
- // Recursively unwrap nested unions
374
- const unwrapped = unwrapNestedUnions(type.types)
375
- result.push(...unwrapped)
376
- } else {
377
- result.push(type)
378
- }
379
- }
380
- return result
381
- }
382
-
383
- const getNonNullTypes = (types: readonly S.AST.AST[]) =>
384
- unwrapNestedUnions(types)
385
- .map(unwrapDeclaration)
386
- .filter((_) => !isNullishType(_))
387
-
388
- const getJsonSchemaAnnotation = (property: S.AST.AST): Record<string, unknown> => {
389
- const jsonSchema = S.AST.resolve(property)?.jsonSchema
390
- return jsonSchema && typeof jsonSchema === "object" ? jsonSchema as Record<string, unknown> : {}
391
- }
392
-
393
- const extractDefaultFromLink = (link: any): unknown | undefined => {
394
- if (!link?.transformation?.decode?.run) return undefined
395
- try {
396
- const result = Effect.runSync(link.transformation.decode.run(Option.none())) as Option.Option<unknown>
397
- return Option.isSome(result) ? result.value : undefined
398
- } catch {
399
- return undefined
400
- }
401
- }
402
-
403
- const getDefaultFromAst = (property: S.AST.AST) => {
404
- // 1. Check withDefaultConstructor (stored in context.defaultValue)
405
- const constructorLink = property.context?.defaultValue?.[0]
406
- const constructorDefault = extractDefaultFromLink(constructorLink)
407
- if (constructorDefault !== undefined) return constructorDefault
408
-
409
- // 2. Check withDecodingDefault (stored in encoding)
410
- const encodingLink = property.encoding?.[0]
411
- if (encodingLink && property.context?.isOptional) {
412
- return extractDefaultFromLink(encodingLink)
413
- }
414
-
415
- return undefined
416
- }
417
-
418
- const getCheckMetas = (property: S.AST.AST): Array<Record<string, any>> => {
419
- const checks = property.checks ?? []
420
-
421
- return checks.flatMap((check) => {
422
- if (check._tag === "FilterGroup") {
423
- return check.checks.flatMap((inner) => {
424
- const meta = inner.annotations?.meta
425
- return meta && typeof meta === "object" ? [meta as Record<string, any>] : []
426
- })
427
- }
428
-
429
- const meta = check.annotations?.meta
430
- return meta && typeof meta === "object" ? [meta as Record<string, any>] : []
431
- })
432
- }
433
-
434
- const getFieldMetadataFromAst = (property: S.AST.AST) => {
435
- const base: Partial<FieldMeta> & Record<string, unknown> = {
436
- description: S.AST.resolveDescription(property)
437
- }
438
- const checks = getCheckMetas(property)
439
-
440
- if (S.AST.isString(property)) {
441
- base.type = "string"
442
- for (const check of checks) {
443
- switch (check._tag) {
444
- case "isMinLength":
445
- base.minLength = check.minLength
446
- break
447
- case "isMaxLength":
448
- base.maxLength = check.maxLength
449
- break
450
- }
451
- }
452
-
453
- if (S.AST.resolveTitle(property) === "Email") {
454
- base.format = "email"
455
- }
456
- } else if (S.AST.isNumber(property)) {
457
- base.type = "number"
458
- for (const check of checks) {
459
- switch (check._tag) {
460
- case "isInt":
461
- base.refinement = "int"
462
- break
463
- case "isGreaterThanOrEqualTo":
464
- base.minimum = check.minimum
465
- break
466
- case "isLessThanOrEqualTo":
467
- base.maximum = check.maximum
468
- break
469
- case "isBetween":
470
- base.minimum = check.minimum
471
- base.maximum = check.maximum
472
- break
473
- case "isGreaterThan":
474
- base.exclusiveMinimum = check.exclusiveMinimum
475
- break
476
- case "isLessThan":
477
- base.exclusiveMaximum = check.exclusiveMaximum
478
- break
479
- }
480
- }
481
- } else if (S.AST.isBoolean(property)) {
482
- base.type = "boolean"
483
- } else if (
484
- S.AST.isDeclaration(property)
485
- && (property.annotations as any)?.typeConstructor?._tag === "Date"
486
- ) {
487
- base.type = "date"
488
- } else {
489
- base.type = "unknown"
490
- }
491
-
492
- return base
493
- }
494
-
495
- export const createMeta = <T = any>(
496
- { meta = {}, parent = "", property, propertySignatures }: CreateMeta,
497
- acc: Partial<MetaRecord<T>> = {}
498
- ): MetaRecord<T> | FieldMeta => {
499
- if (property) {
500
- property = unwrapDeclaration(property)
501
- }
502
-
503
- if (property && S.AST.isObjects(property)) {
504
- return createMeta<T>({
505
- meta,
506
- propertySignatures: property.propertySignatures
507
- })
508
- }
509
-
510
- if (propertySignatures) {
511
- for (const p of propertySignatures) {
512
- const key = parent ? `${parent}.${p.name.toString()}` : p.name.toString()
513
- const nullableOrUndefined = isNullableOrUndefined(p.type)
514
-
515
- // Determine if this field should be required:
516
- // - For nullable discriminated unions, only _tag should be non-required
517
- // - All other fields should calculate their required status normally
518
- let isRequired: boolean
519
- if (meta._isNullableDiscriminatedUnion && p.name.toString() === "_tag") {
520
- // _tag in a nullable discriminated union is not required
521
- isRequired = false
522
- } else if (meta.required === false) {
523
- // Explicitly set to non-required (legacy behavior for backwards compatibility)
524
- isRequired = false
525
- } else {
526
- // Calculate from the property itself
527
- isRequired = !nullableOrUndefined
528
- }
529
-
530
- const typeToProcess = unwrapDeclaration(p.type)
531
- if (S.AST.isUnion(p.type)) {
532
- const nonNullTypes = getNonNullTypes(p.type.types)
533
-
534
- const hasStructMembers = nonNullTypes.some(S.AST.isObjects)
535
-
536
- if (hasStructMembers) {
537
- // Only create parent meta for non-NullOr unions to avoid duplicates
538
- if (!nullableOrUndefined) {
539
- const parentMeta = createMeta<T>({
540
- parent: key,
541
- property: p.type,
542
- meta: { required: isRequired, nullableOrUndefined }
543
- })
544
- acc[key as NestedKeyOf<T>] = parentMeta as FieldMeta
545
- }
546
-
547
- // Process each non-null type and merge their metadata
548
- for (const nonNullType of nonNullTypes) {
549
- if (S.AST.isObjects(nonNullType)) {
550
- // For discriminated unions (multiple branches):
551
- // - If the parent union is nullable, only _tag should be non-required
552
- // - All other fields maintain their normal required status based on their own types
553
- const isNullableDiscriminatedUnion = nullableOrUndefined && nonNullTypes.length > 1
554
-
555
- const branchMeta = createMeta<T>({
556
- parent: key,
557
- propertySignatures: nonNullType.propertySignatures,
558
- meta: isNullableDiscriminatedUnion ? { _isNullableDiscriminatedUnion: true } : {}
559
- })
560
-
561
- // Merge branch metadata, combining select members for shared discriminator fields
562
- for (const [metaKey, metaValue] of Object.entries(branchMeta)) {
563
- const existing = acc[metaKey as NestedKeyOf<T>] as FieldMeta | undefined
564
- if (
565
- existing && existing.type === "select" && (metaValue as any)?.type === "select"
566
- ) {
567
- existing.members = [
568
- ...existing.members,
569
- ...(metaValue as SelectFieldMeta).members.filter(
570
- (m: any) => !existing.members.includes(m)
571
- )
572
- ]
573
- } else {
574
- acc[metaKey as NestedKeyOf<T>] = metaValue as FieldMeta
575
- }
576
- }
577
- }
578
- }
579
- } else {
580
- const arrayTypes = nonNullTypes.filter(S.AST.isArrays)
581
- if (arrayTypes.length > 0) {
582
- const arrayType = arrayTypes[0] // Take the first array type
583
-
584
- acc[key as NestedKeyOf<T>] = {
585
- type: "multiple",
586
- members: arrayType.elements,
587
- rest: arrayType.rest,
588
- required: isRequired,
589
- nullableOrUndefined
590
- } as FieldMeta
591
-
592
- // If the array has struct elements, also create metadata for their properties
593
- if (arrayType.rest && arrayType.rest.length > 0) {
594
- const restElement = unwrapDeclaration(arrayType.rest[0]!)
595
- if (S.AST.isObjects(restElement)) {
596
- for (const prop of restElement.propertySignatures) {
597
- const propKey = `${key}.${prop.name.toString()}`
598
-
599
- const propMeta = createMeta<T>({
600
- parent: propKey,
601
- property: prop.type,
602
- meta: {
603
- required: !isNullableOrUndefined(prop.type),
604
- nullableOrUndefined: isNullableOrUndefined(prop.type)
605
- }
606
- })
607
-
608
- // add to accumulator if valid
609
- if (propMeta && typeof propMeta === "object" && "type" in propMeta) {
610
- acc[propKey as NestedKeyOf<T>] = propMeta as FieldMeta
611
-
612
- if (
613
- propMeta.type === "multiple" && S.AST.isArrays(prop.type) && prop
614
- .type
615
- .rest && prop.type.rest.length > 0
616
- ) {
617
- const nestedRestElement = unwrapDeclaration(prop.type.rest[0]!)
618
- if (S.AST.isObjects(nestedRestElement)) {
619
- for (const nestedProp of nestedRestElement.propertySignatures) {
620
- const nestedPropKey = `${propKey}.${nestedProp.name.toString()}`
621
-
622
- const nestedPropMeta = createMeta<T>({
623
- parent: nestedPropKey,
624
- property: nestedProp.type,
625
- meta: {
626
- required: !isNullableOrUndefined(nestedProp.type),
627
- nullableOrUndefined: isNullableOrUndefined(nestedProp.type)
628
- }
629
- })
630
-
631
- // add to accumulator if valid
632
- if (nestedPropMeta && typeof nestedPropMeta === "object" && "type" in nestedPropMeta) {
633
- acc[nestedPropKey as NestedKeyOf<T>] = nestedPropMeta as FieldMeta
634
- }
635
- }
636
- }
637
- }
638
- }
639
- }
640
- }
641
- }
642
- } else {
643
- // If no struct members and no arrays, process as regular union
644
- const newMeta = createMeta<T>({
645
- parent: key,
646
- property: p.type,
647
- meta: { required: isRequired, nullableOrUndefined }
648
- })
649
- acc[key as NestedKeyOf<T>] = newMeta as FieldMeta
650
- }
651
- }
652
- } else {
653
- if (S.AST.isObjects(typeToProcess)) {
654
- Object.assign(
655
- acc,
656
- createMeta<T>({
657
- parent: key,
658
- propertySignatures: typeToProcess.propertySignatures,
659
- meta: { required: isRequired, nullableOrUndefined }
660
- })
661
- )
662
- } else if (S.AST.isArrays(p.type)) {
663
- // Check if it has struct elements
664
- const hasStructElements = p.type.rest.length > 0
665
- && S.AST.isObjects(unwrapDeclaration(p.type.rest[0]!))
666
-
667
- if (hasStructElements) {
668
- // For arrays with struct elements, only create meta for nested fields, not the array itself
669
- const elementType = unwrapDeclaration(p.type.rest[0]!)
670
- if (S.AST.isObjects(elementType)) {
671
- // Process each property in the array element
672
- for (const prop of elementType.propertySignatures) {
673
- const propKey = `${key}.${prop.name.toString()}`
674
-
675
- // Check if the property is another array
676
- if (S.AST.isArrays(prop.type) && prop.type.rest.length > 0) {
677
- const nestedElementType = unwrapDeclaration(prop.type.rest[0]!)
678
- if (S.AST.isObjects(nestedElementType)) {
679
- // Array with struct elements - process nested fields
680
- for (const nestedProp of nestedElementType.propertySignatures) {
681
- const nestedKey = `${propKey}.${nestedProp.name.toString()}`
682
- const nestedMeta = createMeta<T>({
683
- parent: nestedKey,
684
- property: nestedProp.type,
685
- meta: {
686
- required: !isNullableOrUndefined(nestedProp.type),
687
- nullableOrUndefined: isNullableOrUndefined(nestedProp.type)
688
- }
689
- })
690
- acc[nestedKey as NestedKeyOf<T>] = nestedMeta as FieldMeta
691
- }
692
- } else {
693
- // Array with primitive elements - create meta for the array itself
694
- acc[propKey as NestedKeyOf<T>] = {
695
- type: "multiple",
696
- members: prop.type.elements,
697
- rest: prop.type.rest,
698
- required: !isNullableOrUndefined(prop.type),
699
- nullableOrUndefined: isNullableOrUndefined(prop.type)
700
- } as FieldMeta
701
- }
702
- } else {
703
- const fieldMeta = createMeta<T>({
704
- parent: propKey,
705
- property: prop.type,
706
- meta: {
707
- required: !isNullableOrUndefined(prop.type),
708
- nullableOrUndefined: isNullableOrUndefined(prop.type)
709
- }
710
- })
711
- acc[propKey as NestedKeyOf<T>] = fieldMeta as FieldMeta
712
- }
713
- }
714
- }
715
- } else {
716
- // For arrays with primitive elements, create the array meta
717
- acc[key as NestedKeyOf<T>] = {
718
- type: "multiple",
719
- members: p.type.elements,
720
- rest: p.type.rest,
721
- required: isRequired,
722
- nullableOrUndefined
723
- } as FieldMeta
724
- }
725
- } else {
726
- const newMeta = createMeta<T>({
727
- parent: key,
728
- property: p.type,
729
- meta: {
730
- // an empty string is valid for a S.String field, so we should not mark it as required
731
- // TODO: handle this better via the createMeta minLength parsing
732
- required: isRequired
733
- && (!S.AST.isString(typeToProcess) || !!getFieldMetadataFromAst(p.type).minLength),
734
- nullableOrUndefined
735
- }
736
- })
737
-
738
- acc[key as NestedKeyOf<T>] = newMeta as FieldMeta
739
- }
740
- }
741
- }
742
- return acc
743
- }
744
-
745
- if (property) {
746
- const nullableOrUndefined = getNullableOrUndefined(property)
747
- property = unwrapDeclaration(property)
748
-
749
- if (!Object.hasOwnProperty.call(meta, "required")) {
750
- meta["required"] = !nullableOrUndefined
751
- }
752
-
753
- if (S.AST.isUnion(property)) {
754
- const unwrappedTypes = unwrapNestedUnions(property.types).map(unwrapDeclaration)
755
- const nonNullTypes = unwrappedTypes.filter((t) => !isNullishType(t))
756
-
757
- // Unwrap single-element unions when the literal is a boolean
758
- // (effect-app's S.Literal wraps as S.Literals([x]) → Union([Literal(x)]))
759
- // Don't unwrap string/number literals — they may be discriminator values in a union
760
- if (
761
- nonNullTypes.length === 1
762
- && S.AST.isLiteral(nonNullTypes[0]!)
763
- && typeof nonNullTypes[0]!.literal === "boolean"
764
- ) {
765
- return createMeta<T>({ parent, meta, property: nonNullTypes[0]! })
766
- }
767
-
768
- const nonNullType = nonNullTypes[0]!
769
-
770
- if (S.AST.isObjects(nonNullType)) {
771
- return createMeta<T>({
772
- propertySignatures: nonNullType.propertySignatures,
773
- parent,
774
- meta
775
- })
776
- }
777
-
778
- // TODO: remove after manual _tag deprecation — unwrap legacy S.Struct({ _tag: S.Literal("X") }) pattern
779
- const resolvedTypes = unwrappedTypes.map(unwrapSingleLiteralUnion)
780
- if (resolvedTypes.every((_) => isNullishType(_) || S.AST.isLiteral(_))) {
781
- return {
782
- ...meta,
783
- type: "select",
784
- members: resolvedTypes.filter(S.AST.isLiteral).map((t) => t.literal)
785
- } as FieldMeta
786
- }
787
-
788
- return {
789
- ...meta,
790
- ...createMeta<T>({
791
- parent,
792
- meta,
793
- property: nonNullType
794
- })
795
- } as FieldMeta
796
- }
797
-
798
- if (S.AST.isArrays(property)) {
799
- return {
800
- ...meta,
801
- type: "multiple",
802
- members: property.elements,
803
- rest: property.rest
804
- } as FieldMeta
805
- }
806
-
807
- meta = { ...getJsonSchemaAnnotation(property), ...getFieldMetadataFromAst(property), ...meta }
808
-
809
- return meta as FieldMeta
810
- }
811
-
812
- return acc
813
- }
814
-
815
- // Helper to flatten nested meta structure into dot-notation keys
816
- const flattenMeta = <T>(meta: MetaRecord<T> | FieldMeta, parentKey: string = ""): MetaRecord<T> => {
817
- const result: MetaRecord<T> = {}
818
-
819
- for (const key in meta) {
820
- const value = (meta as any)[key]
821
- const newKey = parentKey ? `${parentKey}.${key}` : key
822
-
823
- if (value && typeof value === "object" && "type" in value) {
824
- result[newKey as DeepKeys<T>] = value as FieldMeta
825
- } else if (value && typeof value === "object") {
826
- Object.assign(result, flattenMeta<T>(value, newKey))
827
- }
828
- }
829
-
830
- return result
831
- }
832
-
833
- const metadataFromAst = <From, To>(
834
- schema: S.Codec<To, From, never>
835
- ): { meta: MetaRecord<To>; defaultValues: Record<string, any>; unionMeta: Record<string, MetaRecord<To>> } => {
836
- const ast = unwrapDeclaration(schema.ast)
837
- const newMeta: MetaRecord<To> = {}
838
- const defaultValues: Record<string, any> = {}
839
- const unionMeta: Record<string, MetaRecord<To>> = {}
840
-
841
- // Handle root-level Union types (discriminated unions)
842
- if (S.AST.isUnion(ast)) {
843
- // Filter out null/undefined types and unwrap transformations
844
- const nonNullTypes = getNonNullTypes(ast.types)
845
-
846
- // Check if this is a discriminated union (all members are structs)
847
- const allStructs = nonNullTypes.every(S.AST.isObjects)
848
-
849
- if (allStructs && nonNullTypes.length > 0) {
850
- // Extract discriminator values from each union member
851
- const discriminatorValues: any[] = []
852
-
853
- // Store metadata for each union member by its tag value
854
- for (const memberType of nonNullTypes) {
855
- if (S.AST.isObjects(memberType)) {
856
- // Find the discriminator field (usually _tag)
857
- const tagProp = memberType.propertySignatures.find(
858
- (p) => p.name.toString() === "_tag"
859
- )
860
-
861
- let tagValue: string | null = null
862
- // TODO: remove after manual _tag deprecation — unwrap legacy S.Struct({ _tag: S.Literal("X") }) pattern
863
- const resolvedTagType = tagProp ? unwrapSingleLiteralUnion(tagProp.type) : null
864
- if (resolvedTagType && S.AST.isLiteral(resolvedTagType)) {
865
- tagValue = resolvedTagType.literal as string
866
- discriminatorValues.push(tagValue)
867
- // Warn if the tag was wrapped in a single-element Union (legacy pattern)
868
- if (
869
- tagProp
870
- && S.AST.isUnion(tagProp.type)
871
- && isDevelopmentEnvironment()
872
- && tagValue != null
873
- && !legacyTagWarningEmittedFor.has(tagValue)
874
- ) {
875
- legacyTagWarningEmittedFor.add(tagValue)
876
- console.warn(
877
- `[OmegaForm] Union member with _tag "${tagValue}" uses S.Struct({ _tag: S.Literal("${tagValue}"), ... }). `
878
- + `Please migrate to S.TaggedStruct("${tagValue}", { ... }) for cleaner AST handling.`
879
- )
880
- }
881
- }
882
-
883
- // Create metadata for this member's properties
884
- const memberMeta = createMeta<To>({
885
- propertySignatures: memberType.propertySignatures
886
- })
887
-
888
- // Store per-tag metadata for reactive lookup
889
- if (tagValue) {
890
- unionMeta[tagValue] = flattenMeta<To>(memberMeta)
891
- }
892
-
893
- // Merge into result (for backward compatibility)
894
- Object.assign(newMeta, memberMeta)
895
- }
896
- }
897
-
898
- // Create metadata for the discriminator field
899
- if (discriminatorValues.length > 0) {
900
- newMeta["_tag" as DeepKeys<To>] = {
901
- type: "select",
902
- members: discriminatorValues,
903
- required: true
904
- } as FieldMeta
905
- }
906
-
907
- return { meta: newMeta, defaultValues, unionMeta }
908
- }
909
- }
910
-
911
- if (S.AST.isObjects(ast)) {
912
- const meta = createMeta<To>({
913
- propertySignatures: ast.propertySignatures
914
- })
915
-
916
- if (Object.values(meta).every((value) => value && "type" in value)) {
917
- return { meta: meta as MetaRecord<To>, defaultValues, unionMeta }
918
- }
919
-
920
- const flattenObject = (
921
- obj: Record<string, any>,
922
- parentKey: string = ""
923
- ) => {
924
- for (const key in obj) {
925
- const newKey = parentKey ? `${parentKey}.${key}` : key
926
- if (obj[key] && typeof obj[key] === "object" && "type" in obj[key]) {
927
- newMeta[newKey as DeepKeys<To>] = obj[key] as FieldMeta
928
- } else if (obj[key] && typeof obj[key] === "object") {
929
- flattenObject(obj[key], newKey)
930
- }
931
- }
932
- }
933
-
934
- flattenObject(meta)
935
- }
936
-
937
- return { meta: newMeta, defaultValues, unionMeta }
938
- }
939
-
940
- export const duplicateSchema = <From, To>(
941
- schema: S.Codec<To, From, never>
942
- ) => {
943
- return schema
944
- }
945
-
946
- export const generateMetaFromSchema = <From, To>(
947
- schema: S.Codec<To, From, never>
948
- ): {
949
- schema: S.Codec<To, From, never>
950
- meta: MetaRecord<To>
951
- unionMeta: Record<string, MetaRecord<To>>
952
- } => {
953
- const { meta, unionMeta } = metadataFromAst(schema)
954
-
955
- return { schema, meta, unionMeta }
956
- }
957
-
958
- export const generateInputStandardSchemaFromFieldMeta = (
959
- meta: FieldMeta,
960
- trans?: ReturnType<typeof useIntl>["trans"]
961
- ): StandardSchemaV1<any, any> => {
962
- if (!trans) {
963
- trans = useIntl().trans
964
- }
965
- let schema: any
966
- switch (meta.type) {
967
- case "string":
968
- schema = meta.format === "email"
969
- ? S.Email.annotate({
970
- message: trans("validation.email.invalid")
971
- })
972
- : S.String.annotate({
973
- message: trans("validation.empty")
974
- })
975
-
976
- if (meta.required) {
977
- schema = schema.check(S.isMinLength(1, {
978
- message: trans("validation.empty")
979
- }))
980
- }
981
-
982
- if (typeof meta.maxLength === "number") {
983
- schema = schema.check(S.isMaxLength(meta.maxLength, {
984
- message: trans("validation.string.maxLength", {
985
- maxLength: meta.maxLength
986
- })
987
- }))
988
- }
989
- if (typeof meta.minLength === "number") {
990
- schema = schema.check(S.isMinLength(meta.minLength, {
991
- message: trans("validation.string.minLength", {
992
- minLength: meta.minLength
993
- })
994
- }))
995
- }
996
- break
997
-
998
- case "number":
999
- if (meta.refinement === "int") {
1000
- schema = S
1001
- .Number
1002
- .annotate({
1003
- message: trans("validation.empty")
1004
- })
1005
- .check(S.isInt({
1006
- message: trans("validation.integer.expected", { actualValue: "NaN" })
1007
- }))
1008
- } else {
1009
- schema = S.Number.annotate({
1010
- message: trans("validation.number.expected", { actualValue: "NaN" })
1011
- })
1012
-
1013
- if (meta.required) {
1014
- schema = schema.annotate({
1015
- message: trans("validation.empty")
1016
- })
1017
- }
1018
- }
1019
-
1020
- if (typeof meta.minimum === "number") {
1021
- schema = schema.check(S.isGreaterThanOrEqualTo(meta.minimum, {
1022
- message: trans(meta.minimum === 0 ? "validation.number.positive" : "validation.number.min", {
1023
- minimum: meta.minimum,
1024
- isExclusive: true
1025
- })
1026
- }))
1027
- }
1028
- if (typeof meta.maximum === "number") {
1029
- schema = schema.check(S.isLessThanOrEqualTo(meta.maximum, {
1030
- message: trans("validation.number.max", {
1031
- maximum: meta.maximum,
1032
- isExclusive: true
1033
- })
1034
- }))
1035
- }
1036
- if (typeof meta.exclusiveMinimum === "number") {
1037
- schema = schema.check(S.isGreaterThan(meta.exclusiveMinimum, {
1038
- message: trans(meta.exclusiveMinimum === 0 ? "validation.number.positive" : "validation.number.min", {
1039
- minimum: meta.exclusiveMinimum,
1040
- isExclusive: false
1041
- })
1042
- }))
1043
- }
1044
- if (typeof meta.exclusiveMaximum === "number") {
1045
- schema = schema.check(S.isLessThan(meta.exclusiveMaximum, {
1046
- message: trans("validation.number.max", {
1047
- maximum: meta.exclusiveMaximum,
1048
- isExclusive: false
1049
- })
1050
- }))
1051
- }
1052
- break
1053
- case "select":
1054
- schema = S.Literals(meta.members as [any, ...any[]]).annotate({
1055
- message: trans("validation.not_a_valid", {
1056
- type: "select",
1057
- message: meta.members.join(", ")
1058
- })
1059
- })
1060
-
1061
- break
1062
-
1063
- case "multiple":
1064
- schema = S.Array(S.String).annotate({
1065
- message: trans("validation.not_a_valid", {
1066
- type: "multiple",
1067
- message: meta.members.join(", ")
1068
- })
1069
- })
1070
- break
1071
-
1072
- case "boolean":
1073
- schema = S.Boolean
1074
- break
1075
-
1076
- case "date":
1077
- schema = S.Date
1078
- break
1079
-
1080
- case "unknown":
1081
- schema = S.Unknown
1082
- break
1083
-
1084
- default:
1085
- // For any unhandled types, use Unknown schema to prevent undefined errors
1086
- console.warn(`Unhandled field type: ${meta}`)
1087
- schema = S.Unknown
1088
- break
1089
- }
1090
- if (!meta.required) {
1091
- schema = S.NullishOr(schema)
1092
- }
1093
- const result = S.toStandardSchemaV1(schema as any)
1094
- return result
1095
- }
1096
-
1097
- export type OmegaAutoGenMeta<
1098
- From extends Record<PropertyKey, any>,
1099
- To extends Record<PropertyKey, any>,
1100
- Name extends DeepKeys<From>
1101
- > = Omit<OmegaInputProps<From, To, Name>, "form">
1102
-
1103
- const supportedInputs = [
1104
- "button",
1105
- "checkbox",
1106
- "color",
1107
- "date",
1108
- "email",
1109
- "number",
1110
- "password",
1111
- "radio",
1112
- "range",
1113
- "search",
1114
- "submit",
1115
- "tel",
1116
- "text",
1117
- "time",
1118
- "url"
1119
- ] as const
1120
- export type SupportedInputs = typeof supportedInputs[number]
1121
- export const getInputType = (input: string): SupportedInputs =>
1122
- (supportedInputs as readonly string[]).includes(input) ? input as SupportedInputs : "text"
1123
-
1124
- export function deepMerge(target: any, source: any) {
1125
- const result = { ...target }
1126
- for (const key in source) {
1127
- if (Array.isArray(source[key])) {
1128
- // Arrays should be copied directly, not deep merged
1129
- result[key] = source[key]
1130
- } else if (source[key] && isObject(source[key])) {
1131
- result[key] = deepMerge(result[key], source[key])
1132
- } else {
1133
- result[key] = source[key]
1134
- }
1135
- }
1136
- return result
1137
- }
1138
-
1139
- // Type definitions for schemas with fields and members
1140
- type SchemaWithFields = {
1141
- fields: Record<string, S.Schema<any>>
1142
- }
1143
-
1144
- type SchemaWithMembers = {
1145
- members: readonly S.Schema<any>[]
1146
- }
1147
-
1148
- // Type guards to check schema types
1149
- function hasFields(schema: any): schema is SchemaWithFields {
1150
- return schema && "fields" in schema && typeof schema.fields === "object"
1151
- }
1152
-
1153
- function hasMembers(schema: any): schema is SchemaWithMembers {
1154
- return schema && "members" in schema && Array.isArray(schema.members)
1155
- }
1156
-
1157
- // Internal implementation with WeakSet tracking
1158
- export const defaultsValueFromSchema = (
1159
- schema: S.Schema<any>,
1160
- record: Record<string, any> = {}
1161
- ): any => {
1162
- const ast = schema.ast
1163
- const defaultValue = getDefaultFromAst(ast)
1164
-
1165
- if (defaultValue !== undefined) {
1166
- return defaultValue
1167
- }
1168
-
1169
- if (isNullableOrUndefined(schema.ast) === "null") {
1170
- return null
1171
- }
1172
- if (isNullableOrUndefined(schema.ast) === "undefined") {
1173
- return undefined
1174
- }
1175
-
1176
- // Check if schema has fields directly
1177
- if (hasFields(schema)) {
1178
- // Process fields and extract default values
1179
- const result: Record<string, any> = {}
1180
-
1181
- for (const [key, fieldSchema] of Object.entries(schema.fields)) {
1182
- const fieldDefault = getDefaultFromAst((fieldSchema as any)?.ast)
1183
- if (fieldDefault !== undefined) {
1184
- result[key] = fieldDefault
1185
- continue
1186
- }
1187
-
1188
- // Recursively process the field
1189
- const fieldValue = defaultsValueFromSchema(fieldSchema as any, record[key] || {})
1190
- if (fieldValue !== undefined) {
1191
- result[key] = fieldValue
1192
- } else if (isNullableOrUndefined((fieldSchema as any).ast) === "undefined") {
1193
- result[key] = undefined
1194
- }
1195
- }
1196
-
1197
- return { ...result, ...record }
1198
- }
1199
-
1200
- // Check if schema has fields in from (for ExtendedClass and similar transformations)
1201
- if ((schema as any)?.from && hasFields((schema as any).from)) {
1202
- return defaultsValueFromSchema((schema as any).from, record)
1203
- }
1204
-
1205
- if (hasMembers(schema)) {
1206
- // Merge all member fields, giving precedence to fields with default values
1207
- const mergedMembers = schema.members.reduce((acc, member) => {
1208
- if (hasFields(member)) {
1209
- // Check each field and give precedence to ones with default values
1210
- Object.entries(member.fields).forEach(([key, fieldSchema]) => {
1211
- const fieldDefault = getDefaultFromAst(fieldSchema.ast)
1212
- const existingDefault = acc[key] ? getDefaultFromAst(acc[key].ast) : undefined
1213
-
1214
- // If field doesn't exist yet, or new field has default and existing doesn't, use new field
1215
- if (!acc[key] || (fieldDefault !== undefined && existingDefault === undefined)) {
1216
- acc[key] = fieldSchema
1217
- }
1218
- // If both have defaults or neither have defaults, keep the first one (existing)
1219
- })
1220
- return acc
1221
- }
1222
- return acc
1223
- }, {} as Record<string, any>)
1224
-
1225
- if (Object.keys(mergedMembers).length === 0) {
1226
- return Object.keys(record).length > 0 ? record : undefined
1227
- }
1228
-
1229
- // Use reduce to properly accumulate the merged fields
1230
- return Object.entries(mergedMembers).reduce((acc, [key, value]) => {
1231
- acc[key] = defaultsValueFromSchema(value, record[key] || {})
1232
- return acc
1233
- }, record)
1234
- }
1235
-
1236
- if (Object.keys(record).length === 0) {
1237
- if (S.AST.isObjects(ast)) {
1238
- // Process TypeLiteral fields directly to build the result object
1239
- const result: Record<string, any> = { ...record }
1240
-
1241
- for (const prop of ast.propertySignatures) {
1242
- const key = prop.name.toString()
1243
- const propType = prop.type
1244
-
1245
- const propDefault = getDefaultFromAst(propType)
1246
- if (propDefault !== undefined) {
1247
- result[key] = propDefault
1248
- continue
1249
- }
1250
-
1251
- // Create a schema from the property type and get its defaults
1252
- const propSchema = S.make(propType)
1253
-
1254
- // Recursively process the property - don't pas for prop processing
1255
- // to allow proper unwrapping of nested structures
1256
- const propValue = defaultsValueFromSchema(propSchema, record[key] || {})
1257
-
1258
- if (propValue !== undefined) {
1259
- result[key] = propValue
1260
- } else if (isNullableOrUndefined(propType) === "undefined") {
1261
- result[key] = undefined
1262
- }
1263
- }
1264
-
1265
- return result
1266
- }
1267
-
1268
- if (S.AST.isString(ast)) {
1269
- return ""
1270
- }
1271
-
1272
- if (S.AST.isBoolean(ast)) {
1273
- return false
1274
- }
1275
- }
1276
- }