@effect-app/vue-components 4.0.0-beta.2 → 4.0.0-beta.200

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 (233) 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 +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 +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 -215
  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 -7
  30. package/dist/vue-components.es.js +29 -44
  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 +27 -31
  105. package/src/components/OmegaForm/OmegaErrorsInternal.vue +3 -4
  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 +21 -11
  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 -895
  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 +9 -10
  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 -237
  137. package/dist/vue-components.es100.js +0 -4
  138. package/dist/vue-components.es11.js +0 -32
  139. package/dist/vue-components.es12.js +0 -439
  140. package/dist/vue-components.es13.js +0 -49
  141. package/dist/vue-components.es14.js +0 -4
  142. package/dist/vue-components.es15.js +0 -4
  143. package/dist/vue-components.es16.js +0 -725
  144. package/dist/vue-components.es17.js +0 -143
  145. package/dist/vue-components.es18.js +0 -6
  146. package/dist/vue-components.es19.js +0 -13
  147. package/dist/vue-components.es2.js +0 -30
  148. package/dist/vue-components.es20.js +0 -5
  149. package/dist/vue-components.es21.js +0 -26
  150. package/dist/vue-components.es22.js +0 -6
  151. package/dist/vue-components.es23.js +0 -10
  152. package/dist/vue-components.es24.js +0 -57
  153. package/dist/vue-components.es25.js +0 -71
  154. package/dist/vue-components.es26.js +0 -8
  155. package/dist/vue-components.es27.js +0 -8
  156. package/dist/vue-components.es28.js +0 -5
  157. package/dist/vue-components.es29.js +0 -5
  158. package/dist/vue-components.es3.js +0 -16
  159. package/dist/vue-components.es30.js +0 -4
  160. package/dist/vue-components.es31.js +0 -4
  161. package/dist/vue-components.es32.js +0 -4
  162. package/dist/vue-components.es33.js +0 -4
  163. package/dist/vue-components.es34.js +0 -19
  164. package/dist/vue-components.es35.js +0 -13
  165. package/dist/vue-components.es36.js +0 -320
  166. package/dist/vue-components.es37.js +0 -563
  167. package/dist/vue-components.es38.js +0 -29
  168. package/dist/vue-components.es39.js +0 -54
  169. package/dist/vue-components.es4.js +0 -52
  170. package/dist/vue-components.es40.js +0 -66
  171. package/dist/vue-components.es41.js +0 -6
  172. package/dist/vue-components.es42.js +0 -6
  173. package/dist/vue-components.es43.js +0 -26
  174. package/dist/vue-components.es44.js +0 -77
  175. package/dist/vue-components.es45.js +0 -42
  176. package/dist/vue-components.es46.js +0 -316
  177. package/dist/vue-components.es47.js +0 -101
  178. package/dist/vue-components.es48.js +0 -33
  179. package/dist/vue-components.es49.js +0 -4
  180. package/dist/vue-components.es5.js +0 -52
  181. package/dist/vue-components.es50.js +0 -4
  182. package/dist/vue-components.es51.js +0 -4
  183. package/dist/vue-components.es52.js +0 -113
  184. package/dist/vue-components.es54.js +0 -9
  185. package/dist/vue-components.es55.js +0 -34
  186. package/dist/vue-components.es57.js +0 -194
  187. package/dist/vue-components.es59.js +0 -40
  188. package/dist/vue-components.es6.js +0 -69
  189. package/dist/vue-components.es60.js +0 -85
  190. package/dist/vue-components.es61.js +0 -43
  191. package/dist/vue-components.es62.js +0 -7
  192. package/dist/vue-components.es63.js +0 -6
  193. package/dist/vue-components.es64.js +0 -25
  194. package/dist/vue-components.es65.js +0 -7
  195. package/dist/vue-components.es66.js +0 -23
  196. package/dist/vue-components.es67.js +0 -32
  197. package/dist/vue-components.es68.js +0 -24
  198. package/dist/vue-components.es69.js +0 -14
  199. package/dist/vue-components.es7.js +0 -83
  200. package/dist/vue-components.es70.js +0 -7
  201. package/dist/vue-components.es71.js +0 -21
  202. package/dist/vue-components.es72.js +0 -11
  203. package/dist/vue-components.es73.js +0 -33
  204. package/dist/vue-components.es74.js +0 -50
  205. package/dist/vue-components.es75.js +0 -28
  206. package/dist/vue-components.es76.js +0 -103
  207. package/dist/vue-components.es77.js +0 -84
  208. package/dist/vue-components.es78.js +0 -23
  209. package/dist/vue-components.es79.js +0 -14
  210. package/dist/vue-components.es8.js +0 -63
  211. package/dist/vue-components.es80.js +0 -115
  212. package/dist/vue-components.es81.js +0 -5
  213. package/dist/vue-components.es82.js +0 -34
  214. package/dist/vue-components.es83.js +0 -4
  215. package/dist/vue-components.es84.js +0 -4
  216. package/dist/vue-components.es85.js +0 -18
  217. package/dist/vue-components.es86.js +0 -17
  218. package/dist/vue-components.es87.js +0 -72
  219. package/dist/vue-components.es88.js +0 -10
  220. package/dist/vue-components.es89.js +0 -4
  221. package/dist/vue-components.es9.js +0 -21
  222. package/dist/vue-components.es90.js +0 -17
  223. package/dist/vue-components.es91.js +0 -13
  224. package/dist/vue-components.es92.js +0 -67
  225. package/dist/vue-components.es93.js +0 -58
  226. package/dist/vue-components.es94.js +0 -19
  227. package/dist/vue-components.es95.js +0 -35
  228. package/dist/vue-components.es96.js +0 -31
  229. package/dist/vue-components.es97.js +0 -44
  230. package/dist/vue-components.es98.js +0 -4
  231. package/dist/vue-components.es99.js +0 -46
  232. package/src/components/OmegaForm/OmegaFormStuff.ts +0 -1174
  233. package/src/constants/index.ts +0 -1
@@ -1,1174 +0,0 @@
1
- import type * as Effect from "effect/Effect"
2
- import * as AST from "effect/SchemaAST"
3
- /* eslint-disable @typescript-eslint/no-explicit-any */
4
- 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"
5
- import { isObject } from "@vueuse/core"
6
- import * as S from "effect/Schema"
7
- import { getTransformationFrom, useIntl } from "../../utils"
8
- import { type OmegaFieldInternalApi } from "./InputProps"
9
- import { type OF, type OmegaFormReturn } from "./useOmegaForm"
10
-
11
- export type FieldPath<T> = unknown extends T ? string
12
- // technically we cannot have primitive at the root
13
- : T extends string | boolean | number | null | undefined | symbol | bigint ? ""
14
- // technically we cannot have array at the root
15
- : T extends ReadonlyArray<infer U> ? FieldPath_<U, `[${number}]`>
16
- : {
17
- [K in keyof T]: FieldPath_<T[K], `${K & string}`>
18
- }[keyof T]
19
-
20
- export type FieldPath_<T, Path extends string> = unknown extends T ? string
21
- : T extends string | boolean | number | null | undefined | symbol | bigint ? Path
22
- : T extends ReadonlyArray<infer U> ? FieldPath_<U, `${Path}[${number}]`> | Path
23
- : {
24
- [K in keyof T]: FieldPath_<T[K], `${Path}.${K & string}`>
25
- }[keyof T]
26
-
27
- export type BaseProps<From, TName extends FieldPath<From>> = {
28
- /**
29
- * Will fallback to i18n when not specified.
30
- * Can also be provided via #label slot for custom HTML labels.
31
- * When using the slot, it receives bindings: { required, id, label }
32
- */
33
- label?: string
34
- validators?: FieldValidators<From>
35
- // Use FlexibleArrayPath: if name contains [], just use TName; otherwise intersect with Leaves<From>
36
- name: TName
37
- /**
38
- * Optional class to apply to the input element.
39
- * - If a string is provided, it will be used instead of the general class
40
- * - If null is provided, no class will be applied (neither inputClass nor general class)
41
- * - If undefined (not provided), the general class will be used
42
- */
43
- inputClass?: string | null
44
- }
45
-
46
- export type TypesWithOptions = "radio" | "select" | "multiple" | "autocomplete" | "autocompletemultiple"
47
- export type DefaultTypeProps = {
48
- type?: TypeOverride
49
- options?: undefined
50
- } | {
51
- type?: TypesWithOptions
52
- // TODO: options should depend on `type`, but since there is auto-type, we can't currently enforce it.
53
- // hence we allow it also for type? (undefined) atm
54
- options?: {
55
- title: string
56
- value: unknown
57
- }[]
58
- }
59
-
60
- export type OmegaInputPropsBase<
61
- From extends Record<PropertyKey, any>,
62
- To extends Record<PropertyKey, any>,
63
- Name extends DeepKeys<From>
64
- > = {
65
- form: OF<From, To> & {
66
- meta: MetaRecord<From>
67
- i18nNamespace?: string
68
- }
69
- } & BaseProps<From, Name>
70
-
71
- export type OmegaInputProps<
72
- From extends Record<PropertyKey, any>,
73
- To extends Record<PropertyKey, any>,
74
- Name extends DeepKeys<From>,
75
- TypeProps = DefaultTypeProps
76
- > = {
77
- form: OmegaFormReturn<From, To, TypeProps> & {
78
- meta: MetaRecord<From>
79
- i18nNamespace?: string
80
- }
81
- } & BaseProps<From, Name>
82
-
83
- export type OmegaArrayProps<
84
- From extends Record<PropertyKey, any>,
85
- To extends Record<PropertyKey, any>,
86
- Name extends DeepKeys<From>
87
- > =
88
- & Omit<
89
- OmegaInputProps<From, To, Name>,
90
- "validators" | "options" | "label" | "type" | "items" | "name"
91
- >
92
- & {
93
- name: DeepKeys<From>
94
- defaultItems?: DeepValue<From, DeepKeys<From>>
95
- // deprecated items, caused bugs in state update, use defaultItems instead. It's not a simple Never, because Volar explodes
96
- items?: "please use `defaultItems` instead"
97
- }
98
-
99
- export type TypeOverride =
100
- | "string"
101
- | "text"
102
- | "number"
103
- | "select"
104
- | "multiple"
105
- | "boolean"
106
- | "radio"
107
- | "autocomplete"
108
- | "autocompletemultiple"
109
- | "switch"
110
- | "range"
111
- | "password"
112
- | "email"
113
- | "date"
114
-
115
- export interface OmegaError {
116
- label: string
117
- inputId: string
118
- errors: readonly string[]
119
- }
120
-
121
- export type FormProps<From, To> =
122
- & Omit<
123
- FormOptions<
124
- From,
125
- FormValidateOrFn<From> | undefined,
126
- FormValidateOrFn<From> | undefined,
127
- StandardSchemaV1<From, To>,
128
- FormValidateOrFn<From> | undefined,
129
- FormAsyncValidateOrFn<From> | undefined,
130
- FormValidateOrFn<From> | undefined,
131
- FormAsyncValidateOrFn<From> | undefined,
132
- FormValidateOrFn<From> | undefined,
133
- FormAsyncValidateOrFn<From> | undefined,
134
- FormAsyncValidateOrFn<From> | undefined,
135
- Record<string, any> | undefined // TODO
136
- >,
137
- | "onSubmit"
138
- | "defaultValues"
139
- >
140
- & {
141
- // when defaultValues are allowed to be undefined, then they should also be allowed to be partial
142
- // 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"
143
- defaultValues?: Partial<From>
144
- onSubmit?: (props: {
145
- formApi: OmegaFormParams<From, To>
146
- meta: any
147
- value: To
148
- }) => Promise<any> | Effect.Effect<unknown, any, never>
149
- }
150
-
151
- export type OmegaFormParams<From, To> = FormApi<
152
- From,
153
- FormValidateOrFn<From> | undefined,
154
- FormValidateOrFn<From> | undefined,
155
- StandardSchemaV1<From, To>,
156
- FormValidateOrFn<From> | undefined,
157
- FormAsyncValidateOrFn<From> | undefined,
158
- FormValidateOrFn<From> | undefined,
159
- FormAsyncValidateOrFn<From> | undefined,
160
- FormValidateOrFn<From> | undefined,
161
- FormAsyncValidateOrFn<From> | undefined,
162
- FormAsyncValidateOrFn<From> | undefined,
163
- Record<string, any> | undefined
164
- >
165
-
166
- export type OmegaFormState<From, To> = FormState<
167
- From,
168
- FormValidateOrFn<From> | undefined,
169
- FormValidateOrFn<From> | undefined,
170
- StandardSchemaV1<From, To>,
171
- FormValidateOrFn<From> | undefined,
172
- FormAsyncValidateOrFn<From> | undefined,
173
- FormValidateOrFn<From> | undefined,
174
- FormAsyncValidateOrFn<From> | undefined,
175
- FormValidateOrFn<From> | undefined,
176
- FormAsyncValidateOrFn<From> | undefined,
177
- FormAsyncValidateOrFn<From> | undefined
178
- >
179
-
180
- // TODO: stitch TSubmitMeta somehow
181
- export type OmegaFormApi<From, To, TSubmitMeta = Record<string, any> | undefined> =
182
- & OmegaFormParams<From, To>
183
- & VueFormApi<
184
- From,
185
- FormValidateOrFn<From> | undefined,
186
- FormValidateOrFn<From> | undefined,
187
- StandardSchemaV1<From, To>,
188
- FormValidateOrFn<From> | undefined,
189
- FormAsyncValidateOrFn<From> | undefined,
190
- FormValidateOrFn<From> | undefined,
191
- FormAsyncValidateOrFn<From> | undefined,
192
- FormValidateOrFn<From> | undefined,
193
- FormAsyncValidateOrFn<From> | undefined,
194
- FormAsyncValidateOrFn<From> | undefined,
195
- TSubmitMeta
196
- >
197
-
198
- export type FormComponent<T, S> = VueFormApi<
199
- T,
200
- FormValidateOrFn<T> | undefined,
201
- FormValidateOrFn<T> | undefined,
202
- StandardSchemaV1<T, S>,
203
- FormValidateOrFn<T> | undefined,
204
- FormAsyncValidateOrFn<T> | undefined,
205
- FormValidateOrFn<T> | undefined,
206
- FormAsyncValidateOrFn<T> | undefined,
207
- FormValidateOrFn<T> | undefined,
208
- FormAsyncValidateOrFn<T> | undefined,
209
- FormAsyncValidateOrFn<T> | undefined,
210
- Record<string, any> | undefined
211
- >
212
-
213
- export type FormType<
214
- From extends Record<PropertyKey, any>,
215
- To extends Record<PropertyKey, any>,
216
- Name extends DeepKeys<From>
217
- > = OmegaFormApi<From, To> & {
218
- Field: OmegaFieldInternalApi<From, Name>
219
- }
220
-
221
- export type PrefixFromDepth<
222
- K extends string | number,
223
- _TDepth extends any[]
224
- > = K
225
-
226
- export type NestedKeyOf<T> = DeepKeys<T>
227
-
228
- export type FieldValidators<T> = {
229
- onChangeAsync?: FieldAsyncValidateOrFn<T, any, any>
230
- onChange?: FieldValidateOrFn<T, any, any>
231
- onBlur?: FieldValidateOrFn<T, any, any>
232
- onBlurAsync?: FieldAsyncValidateOrFn<T, any, any>
233
- }
234
-
235
- // Field metadata type definitions
236
- export type BaseFieldMeta = {
237
- required: boolean
238
- nullableOrUndefined?: false | "undefined" | "null"
239
- }
240
-
241
- export type StringFieldMeta = BaseFieldMeta & {
242
- type: "string"
243
- maxLength?: number
244
- minLength?: number
245
- format?: string
246
- }
247
-
248
- export type NumberFieldMeta = BaseFieldMeta & {
249
- type: "number"
250
- minimum?: number
251
- maximum?: number
252
- exclusiveMinimum?: number
253
- exclusiveMaximum?: number
254
- refinement?: "int"
255
- }
256
-
257
- export type SelectFieldMeta = BaseFieldMeta & {
258
- type: "select"
259
- members: any[] // TODO: should be non empty array?
260
- }
261
-
262
- export type MultipleFieldMeta = BaseFieldMeta & {
263
- type: "multiple"
264
- members: any[] // TODO: should be non empty array?
265
- rest: readonly AST.AST[]
266
- }
267
-
268
- export type BooleanFieldMeta = BaseFieldMeta & {
269
- type: "boolean"
270
- }
271
-
272
- export type UnknownFieldMeta = BaseFieldMeta & {
273
- type: "unknown"
274
- }
275
-
276
- export type FieldMeta =
277
- | StringFieldMeta
278
- | NumberFieldMeta
279
- | SelectFieldMeta
280
- | MultipleFieldMeta
281
- | BooleanFieldMeta
282
- | UnknownFieldMeta
283
-
284
- export type MetaRecord<T = string> = {
285
- [K in NestedKeyOf<T>]?: FieldMeta
286
- }
287
-
288
- export type FilterItems = {
289
- items: readonly [string, ...string[]]
290
- message:
291
- | string
292
- | Effect.Effect<string, never, never>
293
- | { readonly message: string | Effect.Effect<string> }
294
- }
295
-
296
- export type CreateMeta =
297
- & {
298
- parent?: string
299
- meta?: Record<string, any>
300
- nullableOrUndefined?: false | "undefined" | "null"
301
- }
302
- & (
303
- | {
304
- propertySignatures: readonly AST.PropertySignature[]
305
- property?: never
306
- }
307
- | {
308
- propertySignatures?: never
309
- property: AST.AST
310
- }
311
- )
312
-
313
- const getNullableOrUndefined = (property: AST.AST) => {
314
- if (!AST.isUnion(property)) return false
315
- return property.types.find((_) => AST.isUndefined(_) || _ === S.Null.ast)
316
- }
317
-
318
- export const isNullableOrUndefined = (property: false | AST.AST | undefined) => {
319
- if (!property || !AST.isUnion(property)) return false
320
- if (property.types.find((_) => AST.isUndefined(_))) {
321
- return "undefined"
322
- }
323
- if (property.types.find((_) => _ === S.Null.ast)) return "null"
324
- return false
325
- }
326
-
327
- // Helper function to recursively unwrap nested unions (e.g., S.NullOr(S.NullOr(X)) -> X)
328
- const unwrapNestedUnions = (types: readonly AST.AST[]): readonly AST.AST[] => {
329
- const result: AST.AST[] = []
330
- for (const type of types) {
331
- if (AST.isUnion(type)) {
332
- // Recursively unwrap nested unions
333
- const unwrapped = unwrapNestedUnions(type.types)
334
- result.push(...unwrapped)
335
- } else {
336
- result.push(type)
337
- }
338
- }
339
- return result
340
- }
341
-
342
- export const createMeta = <T = any>(
343
- { meta = {}, parent = "", property, propertySignatures }: CreateMeta,
344
- acc: Partial<MetaRecord<T>> = {}
345
- ): MetaRecord<T> | FieldMeta => {
346
- // unwraps class (Class are transformations)
347
- // this calls createMeta recursively, so wrapped transformations are also unwrapped
348
- // BUT: check for Int title annotation first - S.Int and branded Int have title "Int" or "int"
349
- // and we don't want to lose that information by unwrapping
350
- if (property && AST.isDeclaration(property)) {
351
- const titleOnTransform = property.annotations?.title ?? ""
352
-
353
- // only unwrap if this is NOT an Int type
354
- if (titleOnTransform !== "Int" && titleOnTransform !== "int") {
355
- // In v4, Declaration doesn't have a 'from' property
356
- // Just return the property as-is
357
- return createMeta<T>({
358
- parent,
359
- meta,
360
- property
361
- })
362
- }
363
- // if it's Int, fall through to process it with the Int type
364
- }
365
-
366
- if (property && AST.isObjects(property)) {
367
- return createMeta<T>({
368
- meta,
369
- propertySignatures: property.propertySignatures
370
- })
371
- }
372
-
373
- if (propertySignatures) {
374
- for (const p of propertySignatures) {
375
- const key = parent ? `${parent}.${p.name.toString()}` : p.name.toString()
376
- const nullableOrUndefined = isNullableOrUndefined(p.type)
377
-
378
- // Check if this property has title "Int" or "int" annotation (from Int brand wrapper)
379
- const propertyTitle = p.type.annotations?.title ?? ""
380
- const isIntField = propertyTitle === "Int" || propertyTitle === "int"
381
-
382
- // Determine if this field should be required:
383
- // - For nullable discriminated unions, only _tag should be non-required
384
- // - All other fields should calculate their required status normally
385
- let isRequired: boolean
386
- if (meta._isNullableDiscriminatedUnion && p.name.toString() === "_tag") {
387
- // _tag in a nullable discriminated union is not required
388
- isRequired = false
389
- } else if (meta.required === false) {
390
- // Explicitly set to non-required (legacy behavior for backwards compatibility)
391
- isRequired = false
392
- } else {
393
- // Calculate from the property itself
394
- isRequired = !nullableOrUndefined
395
- }
396
-
397
- const typeToProcess = p.type
398
- if (AST.isUnion(p.type)) {
399
- // First unwrap any nested unions, then filter out null/undefined
400
- const unwrappedTypes = unwrapNestedUnions(p.type.types)
401
- const nonNullTypes = unwrappedTypes
402
- .filter(
403
- (t) => !AST.isUndefined(t) && !AST.isNull(t)
404
- )
405
- // unwraps class (Class are transformations)
406
- .map(getTransformationFrom)
407
-
408
- const hasStructMembers = nonNullTypes.some(
409
- (t) => AST.isObjects(t)
410
- )
411
-
412
- if (hasStructMembers) {
413
- // Only create parent meta for non-NullOr unions to avoid duplicates
414
- if (!nullableOrUndefined) {
415
- const parentMeta = createMeta<T>({
416
- parent: key,
417
- property: p.type,
418
- meta: { required: isRequired, nullableOrUndefined }
419
- })
420
- acc[key as NestedKeyOf<T>] = parentMeta as FieldMeta
421
- }
422
-
423
- // Process each non-null type and merge their metadata
424
- for (const nonNullType of nonNullTypes) {
425
- if (AST.isObjects(nonNullType)) {
426
- // For discriminated unions (multiple branches):
427
- // - If the parent union is nullable, only _tag should be non-required
428
- // - All other fields maintain their normal required status based on their own types
429
- const isNullableDiscriminatedUnion = nullableOrUndefined && nonNullTypes.length > 1
430
-
431
- Object.assign(
432
- acc,
433
- createMeta<T>({
434
- parent: key,
435
- propertySignatures: nonNullType.propertySignatures,
436
- meta: isNullableDiscriminatedUnion ? { _isNullableDiscriminatedUnion: true } : {}
437
- })
438
- )
439
- }
440
- }
441
- } else {
442
- // Check if any of the union types are arrays
443
- const arrayTypes = nonNullTypes.filter(AST.isArrays)
444
- if (arrayTypes.length > 0) {
445
- const arrayType = arrayTypes[0] // Take the first array type
446
-
447
- acc[key as NestedKeyOf<T>] = {
448
- type: "multiple",
449
- members: arrayType.elements,
450
- rest: arrayType.rest,
451
- required: isRequired,
452
- nullableOrUndefined
453
- } as FieldMeta
454
-
455
- // If the array has struct elements, also create metadata for their properties
456
- if (arrayType.rest && arrayType.rest.length > 0) {
457
- const restElement = arrayType.rest[0]
458
- if (AST.isObjects(restElement)) {
459
- for (const prop of restElement.propertySignatures) {
460
- const propKey = `${key}.${prop.name.toString()}`
461
-
462
- const propMeta = createMeta<T>({
463
- parent: propKey,
464
- property: prop.type,
465
- meta: {
466
- required: !isNullableOrUndefined(prop.type),
467
- nullableOrUndefined: isNullableOrUndefined(prop.type)
468
- }
469
- })
470
-
471
- // add to accumulator if valid
472
- if (propMeta && typeof propMeta === "object" && "type" in propMeta) {
473
- acc[propKey as NestedKeyOf<T>] = propMeta as FieldMeta
474
-
475
- if (
476
- propMeta.type === "multiple" && AST.isArrays(prop.type) && prop
477
- .type
478
- .rest && prop.type.rest.length > 0
479
- ) {
480
- const nestedRestElement = prop.type.rest[0]
481
- if (AST.isObjects(nestedRestElement)) {
482
- for (const nestedProp of nestedRestElement.propertySignatures) {
483
- const nestedPropKey = `${propKey}.${nestedProp.name.toString()}`
484
-
485
- const nestedPropMeta = createMeta<T>({
486
- parent: nestedPropKey,
487
- property: nestedProp.type,
488
- meta: {
489
- required: !isNullableOrUndefined(nestedProp.type),
490
- nullableOrUndefined: isNullableOrUndefined(nestedProp.type)
491
- }
492
- })
493
-
494
- // add to accumulator if valid
495
- if (nestedPropMeta && typeof nestedPropMeta === "object" && "type" in nestedPropMeta) {
496
- acc[nestedPropKey as NestedKeyOf<T>] = nestedPropMeta as FieldMeta
497
- }
498
- }
499
- }
500
- }
501
- }
502
- }
503
- }
504
- }
505
- } else {
506
- // If no struct members and no arrays, process as regular union
507
- const newMeta = createMeta<T>({
508
- parent: key,
509
- property: p.type,
510
- meta: { required: isRequired, nullableOrUndefined }
511
- })
512
- acc[key as NestedKeyOf<T>] = newMeta as FieldMeta
513
- }
514
- }
515
- } else {
516
- // Unwrap transformations (like ExtendedClass) to check for propertySignatures
517
- const unwrappedTypeToProcess = getTransformationFrom(typeToProcess)
518
- if (AST.isObjects(unwrappedTypeToProcess)) {
519
- Object.assign(
520
- acc,
521
- createMeta<T>({
522
- parent: key,
523
- propertySignatures: unwrappedTypeToProcess.propertySignatures,
524
- meta: { required: isRequired, nullableOrUndefined }
525
- })
526
- )
527
- } else if (AST.isArrays(p.type)) {
528
- // Check if it has struct elements
529
- const hasStructElements = p.type.rest.length > 0
530
- && AST.isObjects(p.type.rest[0])
531
-
532
- if (hasStructElements) {
533
- // For arrays with struct elements, only create meta for nested fields, not the array itself
534
- const elementType = p.type.rest[0]
535
- if (AST.isObjects(elementType)) {
536
- // Process each property in the array element
537
- for (const prop of elementType.propertySignatures) {
538
- const propKey = `${key}.${prop.name.toString()}`
539
-
540
- // Check if the property is another array
541
- if (AST.isArrays(prop.type) && prop.type.rest.length > 0) {
542
- const nestedElementType = prop.type.rest[0]
543
- if (AST.isObjects(nestedElementType)) {
544
- // Array with struct elements - process nested fields
545
- for (const nestedProp of nestedElementType.propertySignatures) {
546
- const nestedKey = `${propKey}.${nestedProp.name.toString()}`
547
- const nestedMeta = createMeta<T>({
548
- parent: nestedKey,
549
- property: nestedProp.type,
550
- meta: {
551
- required: !isNullableOrUndefined(nestedProp.type),
552
- nullableOrUndefined: isNullableOrUndefined(nestedProp.type)
553
- }
554
- })
555
- acc[nestedKey as NestedKeyOf<T>] = nestedMeta as FieldMeta
556
- }
557
- } else {
558
- // Array with primitive elements - create meta for the array itself
559
- acc[propKey as NestedKeyOf<T>] = {
560
- type: "multiple",
561
- members: prop.type.elements,
562
- rest: prop.type.rest,
563
- required: !isNullableOrUndefined(prop.type),
564
- nullableOrUndefined: isNullableOrUndefined(prop.type)
565
- } as FieldMeta
566
- }
567
- } else {
568
- const fieldMeta = createMeta<T>({
569
- parent: propKey,
570
- property: prop.type,
571
- meta: {
572
- required: !isNullableOrUndefined(prop.type),
573
- nullableOrUndefined: isNullableOrUndefined(prop.type)
574
- }
575
- })
576
- acc[propKey as NestedKeyOf<T>] = fieldMeta as FieldMeta
577
- }
578
- }
579
- }
580
- } else {
581
- // For arrays with primitive elements, create the array meta
582
- acc[key as NestedKeyOf<T>] = {
583
- type: "multiple",
584
- members: p.type.elements,
585
- rest: p.type.rest,
586
- required: isRequired,
587
- nullableOrUndefined
588
- } as FieldMeta
589
- }
590
- } else {
591
- const newMeta = createMeta<T>({
592
- parent: key,
593
- property: p.type,
594
- meta: {
595
- required: isRequired,
596
- nullableOrUndefined,
597
- ...(isIntField && { refinement: "int" })
598
- }
599
- })
600
-
601
- acc[key as NestedKeyOf<T>] = newMeta as FieldMeta
602
- }
603
- }
604
- }
605
- return acc
606
- }
607
-
608
- if (property) {
609
- const nullableOrUndefined = getNullableOrUndefined(property)
610
-
611
- if (!Object.hasOwnProperty.call(meta, "required")) {
612
- meta["required"] = !nullableOrUndefined
613
- }
614
-
615
- if (AST.isUnion(property)) {
616
- // First unwrap any nested unions, then filter out null/undefined
617
- const unwrappedTypes = unwrapNestedUnions(property.types)
618
- const nonNullType = unwrappedTypes.find(
619
- (t) => !AST.isUndefined(t) && !AST.isNull(t)
620
- )!
621
-
622
- if (AST.isObjects(nonNullType)) {
623
- return createMeta<T>({
624
- propertySignatures: nonNullType.propertySignatures,
625
- parent,
626
- meta
627
- })
628
- }
629
-
630
- if (unwrappedTypes.every(AST.isLiteral)) {
631
- return {
632
- ...meta,
633
- type: "select",
634
- members: unwrappedTypes.map((t) => t.literal)
635
- } as FieldMeta
636
- }
637
-
638
- return {
639
- ...meta,
640
- ...createMeta<T>({
641
- parent,
642
- meta,
643
- property: nonNullType
644
- })
645
- } as FieldMeta
646
- }
647
-
648
- if (AST.isArrays(property)) {
649
- return {
650
- ...meta,
651
- type: "multiple",
652
- members: property.elements,
653
- rest: property.rest
654
- } as FieldMeta
655
- }
656
-
657
- const JSONAnnotation = (property.annotations?.jsonSchema ?? {}) as Record<string, unknown>
658
-
659
- meta = { ...JSONAnnotation, ...meta }
660
-
661
- // check the title annotation BEFORE following "from" to detect refinements like S.Int
662
- let titleType = property.annotations?.title ?? "unknown"
663
-
664
- // Detect basic types from AST if no title annotation
665
- if (titleType === "unknown") {
666
- if (AST.isString(property)) {
667
- titleType = "string"
668
- } else if (AST.isNumber(property)) {
669
- titleType = "number"
670
- } else if (AST.isBoolean(property)) {
671
- titleType = "boolean"
672
- }
673
- }
674
-
675
- // if this is S.Int (a refinement), set the type and skip following "from"
676
- // otherwise we'd lose the "Int" information and get "number" instead
677
- if (titleType === "Int" || titleType === "int") {
678
- meta["type"] = "number"
679
- meta["refinement"] = "int"
680
- // don't follow "from" for Int refinements
681
- } else {
682
- meta["type"] = titleType
683
- }
684
-
685
- // Always ensure required is set before returning
686
- if (!Object.hasOwnProperty.call(meta, "required")) {
687
- meta["required"] = !nullableOrUndefined
688
- }
689
-
690
- return meta as FieldMeta
691
- }
692
-
693
- return acc
694
- }
695
-
696
- // Helper to flatten nested meta structure into dot-notation keys
697
- const flattenMeta = <T>(meta: MetaRecord<T> | FieldMeta, parentKey: string = ""): MetaRecord<T> => {
698
- const result: MetaRecord<T> = {}
699
-
700
- for (const key in meta) {
701
- const value = (meta as any)[key]
702
- const newKey = parentKey ? `${parentKey}.${key}` : key
703
-
704
- if (value && typeof value === "object" && "type" in value) {
705
- result[newKey as DeepKeys<T>] = value as FieldMeta
706
- } else if (value && typeof value === "object") {
707
- Object.assign(result, flattenMeta<T>(value, newKey))
708
- }
709
- }
710
-
711
- return result
712
- }
713
-
714
- const _schemaFromAst = (ast: AST.AST): S.Codec<any> => S.make(ast)
715
-
716
- const metadataFromAst = <_From, To>(
717
- schema: any // v4 Schema type is complex, use any for now
718
- ): { meta: MetaRecord<To>; defaultValues: Record<string, any>; unionMeta: Record<string, MetaRecord<To>> } => {
719
- const ast = schema.ast
720
- const newMeta: MetaRecord<To> = {}
721
- const defaultValues: Record<string, any> = {}
722
- const unionMeta: Record<string, MetaRecord<To>> = {}
723
-
724
- // Handle root-level Union types (discriminated unions)
725
- if (AST.isUnion(ast)) {
726
- const types = ast.types
727
-
728
- // Filter out null/undefined types and unwrap transformations
729
- const nonNullTypes = types
730
- .filter((t: any) => !AST.isUndefined(t) && !AST.isNull(t))
731
- .map(getTransformationFrom)
732
-
733
- // Check if this is a discriminated union (all members are structs)
734
- const allStructs = nonNullTypes.every((t: any) => AST.isObjects(t))
735
-
736
- if (allStructs && nonNullTypes.length > 0) {
737
- // Extract discriminator values from each union member
738
- const discriminatorValues: any[] = []
739
-
740
- // Store metadata for each union member by its tag value
741
- for (const memberType of nonNullTypes) {
742
- if (AST.isObjects(memberType)) {
743
- // Find the discriminator field (usually _tag)
744
- const tagProp = memberType.propertySignatures.find(
745
- (p: any) => p.name.toString() === "_tag"
746
- )
747
-
748
- let tagValue: string | null = null
749
- if (tagProp && AST.isLiteral(tagProp.type)) {
750
- tagValue = tagProp.type.literal as string
751
- discriminatorValues.push(tagValue)
752
- }
753
-
754
- // Create metadata for this member's properties
755
- const memberMeta = createMeta<To>({
756
- propertySignatures: memberType.propertySignatures
757
- })
758
-
759
- // Store per-tag metadata for reactive lookup
760
- if (tagValue) {
761
- unionMeta[tagValue] = flattenMeta<To>(memberMeta)
762
- }
763
-
764
- // Merge into result (for backward compatibility)
765
- Object.assign(newMeta, memberMeta)
766
- }
767
- }
768
-
769
- // Create metadata for the discriminator field
770
- if (discriminatorValues.length > 0) {
771
- newMeta["_tag" as DeepKeys<To>] = {
772
- type: "select",
773
- members: discriminatorValues,
774
- required: true
775
- } as FieldMeta
776
- }
777
-
778
- return { meta: newMeta, defaultValues, unionMeta }
779
- }
780
- }
781
-
782
- if (AST.isObjects(ast)) {
783
- const meta = createMeta<To>({
784
- propertySignatures: ast.propertySignatures
785
- })
786
-
787
- if (Object.values(meta).every((value) => value && "type" in value)) {
788
- return { meta: meta as MetaRecord<To>, defaultValues, unionMeta }
789
- }
790
-
791
- const flattenObject = (
792
- obj: Record<string, any>,
793
- parentKey: string = ""
794
- ) => {
795
- for (const key in obj) {
796
- const newKey = parentKey ? `${parentKey}.${key}` : key
797
- if (obj[key] && typeof obj[key] === "object" && "type" in obj[key]) {
798
- newMeta[newKey as DeepKeys<To>] = obj[key] as FieldMeta
799
- } else if (obj[key] && typeof obj[key] === "object") {
800
- flattenObject(obj[key], newKey)
801
- }
802
- }
803
- }
804
-
805
- flattenObject(meta)
806
- }
807
-
808
- return { meta: newMeta, defaultValues, unionMeta }
809
- }
810
-
811
- export const duplicateSchema = <From, To>(
812
- schema: S.Codec<To, From, never>
813
- ) => {
814
- return schema
815
- }
816
-
817
- export const generateMetaFromSchema = <_From, To>(
818
- schema: any // v4 Schema type is complex, use any for now
819
- ): {
820
- schema: any
821
- meta: MetaRecord<To>
822
- unionMeta: Record<string, MetaRecord<To>>
823
- } => {
824
- const { meta, unionMeta } = metadataFromAst(schema)
825
-
826
- return { schema, meta, unionMeta }
827
- }
828
-
829
- export const generateInputStandardSchemaFromFieldMeta = (
830
- meta: FieldMeta,
831
- trans?: ReturnType<typeof useIntl>["trans"]
832
- ): StandardSchemaV1<any, any> => {
833
- if (!trans) {
834
- trans = useIntl().trans
835
- }
836
- let schema: S.Codec<any>
837
-
838
- switch (meta.type) {
839
- case "string": {
840
- schema = S.String
841
-
842
- // Apply format-specific schemas
843
- if (meta.format === "email") {
844
- // v4 doesn't have S.Email, use pattern validation
845
- schema = S.String.check(
846
- S.makeFilter(
847
- (s) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s) || trans("validation.email.invalid"),
848
- { title: "email format" }
849
- )
850
- )
851
- }
852
-
853
- // Apply length validations
854
- if (meta.required || typeof meta.minLength === "number") {
855
- const minLen = meta.required ? Math.max(1, meta.minLength || 0) : (meta.minLength || 0)
856
- if (minLen > 0) {
857
- schema = schema.check(
858
- S.makeFilter(
859
- (s) => s.length >= minLen || trans("validation.string.minLength", { minLength: minLen }),
860
- { title: `minLength(${minLen})` }
861
- )
862
- )
863
- }
864
- }
865
-
866
- if (typeof meta.maxLength === "number") {
867
- schema = schema.check(
868
- S.makeFilter(
869
- (s) => s.length <= meta.maxLength! || trans("validation.string.maxLength", { maxLength: meta.maxLength }),
870
- { title: `maxLength(${meta.maxLength})` }
871
- )
872
- )
873
- }
874
- break
875
- }
876
-
877
- case "number": {
878
- if (meta.refinement === "int") {
879
- schema = S.Int
880
- } else {
881
- schema = S.Number
882
- }
883
-
884
- // Apply numeric validations
885
- if (typeof meta.minimum === "number") {
886
- schema = schema.check(
887
- S.makeFilter(
888
- (n) =>
889
- n >= meta.minimum! || trans(
890
- meta.minimum === 0 ? "validation.number.positive" : "validation.number.min",
891
- { minimum: meta.minimum, isExclusive: false }
892
- ),
893
- { title: `>=${meta.minimum}` }
894
- )
895
- )
896
- }
897
-
898
- if (typeof meta.maximum === "number") {
899
- schema = schema.check(
900
- S.makeFilter(
901
- (n) =>
902
- n <= meta.maximum! || trans("validation.number.max", {
903
- maximum: meta.maximum,
904
- isExclusive: false
905
- }),
906
- { title: `<=${meta.maximum}` }
907
- )
908
- )
909
- }
910
-
911
- if (typeof meta.exclusiveMinimum === "number") {
912
- schema = schema.check(
913
- S.makeFilter(
914
- (n) =>
915
- n > meta.exclusiveMinimum! || trans(
916
- meta.exclusiveMinimum === 0 ? "validation.number.positive" : "validation.number.min",
917
- { minimum: meta.exclusiveMinimum, isExclusive: true }
918
- ),
919
- { title: `>${meta.exclusiveMinimum}` }
920
- )
921
- )
922
- }
923
-
924
- if (typeof meta.exclusiveMaximum === "number") {
925
- schema = schema.check(
926
- S.makeFilter(
927
- (n) =>
928
- n < meta.exclusiveMaximum! || trans("validation.number.max", {
929
- maximum: meta.exclusiveMaximum,
930
- isExclusive: true
931
- }),
932
- { title: `<${meta.exclusiveMaximum}` }
933
- )
934
- )
935
- }
936
- break
937
- }
938
-
939
- case "select": {
940
- // Use Literal for select options
941
- if (meta.members.length === 0) {
942
- schema = S.Unknown
943
- } else if (meta.members.length === 1) {
944
- schema = S.Literal(meta.members[0])
945
- } else {
946
- // v4 Union accepts an array of schemas
947
- schema = S.Union(meta.members.map((m) => S.Literal(m)))
948
- }
949
- break
950
- }
951
-
952
- case "multiple": {
953
- schema = S.Array(S.String)
954
- break
955
- }
956
-
957
- case "boolean": {
958
- schema = S.Boolean
959
- break
960
- }
961
-
962
- case "unknown": {
963
- schema = S.Unknown
964
- break
965
- }
966
-
967
- default: {
968
- console.warn(`Unhandled field type: ${(meta as any).type}`)
969
- schema = S.Unknown
970
- break
971
- }
972
- }
973
-
974
- // Wrap in union with null/undefined if not required
975
- if (!meta.required) {
976
- // v4 Union takes an array of schemas
977
- schema = S.Union([schema, S.Null, S.Undefined])
978
- }
979
-
980
- return S.toStandardSchemaV1(schema as any)
981
- }
982
-
983
- // TODO: Fix v4 migration - nullableInput transformation needs proper type handling
984
- // export const nullableInput = <A>(
985
- // schema: S.Codec<A>,
986
- // defaultValue: () => A
987
- // ): S.Codec<A> =>
988
- // S.NullOr(schema).pipe(
989
- // S.decodeTo(
990
- // schema,
991
- // SchemaTransformation.transform({
992
- // decode: (input: A | null) => input ?? defaultValue(),
993
- // encode: (output: A) => output
994
- // })
995
- // )
996
- // )
997
-
998
- export type OmegaAutoGenMeta<
999
- From extends Record<PropertyKey, any>,
1000
- To extends Record<PropertyKey, any>,
1001
- Name extends DeepKeys<From>
1002
- > = Omit<OmegaInputProps<From, To, Name>, "form">
1003
-
1004
- const supportedInputs = [
1005
- "button",
1006
- "checkbox",
1007
- "color",
1008
- "date",
1009
- "email",
1010
- "number",
1011
- "password",
1012
- "radio",
1013
- "range",
1014
- "search",
1015
- "submit",
1016
- "tel",
1017
- "text",
1018
- "time",
1019
- "url"
1020
- ] as const
1021
- export type SupportedInputs = typeof supportedInputs[number]
1022
- export const getInputType = (input: string): SupportedInputs =>
1023
- (supportedInputs as readonly string[]).includes(input) ? input as SupportedInputs : "text"
1024
-
1025
- export function deepMerge(target: any, source: any) {
1026
- const result = { ...target }
1027
- for (const key in source) {
1028
- if (Array.isArray(source[key])) {
1029
- // Arrays should be copied directly, not deep merged
1030
- result[key] = source[key]
1031
- } else if (source[key] && isObject(source[key])) {
1032
- result[key] = deepMerge(result[key], source[key])
1033
- } else {
1034
- result[key] = source[key]
1035
- }
1036
- }
1037
- return result
1038
- }
1039
-
1040
- // Type definitions for schemas with fields and members
1041
- type SchemaWithFields = {
1042
- fields: Record<string, S.Top>
1043
- }
1044
-
1045
- type SchemaWithMembers = {
1046
- members: readonly S.Top[]
1047
- }
1048
-
1049
- // Type guards to check schema types
1050
- function hasFields(schema: any): schema is SchemaWithFields {
1051
- return schema && "fields" in schema && typeof schema.fields === "object"
1052
- }
1053
-
1054
- function hasMembers(schema: any): schema is SchemaWithMembers {
1055
- return schema && "members" in schema && Array.isArray(schema.members)
1056
- }
1057
-
1058
- // Internal implementation with WeakSet tracking
1059
- export const defaultsValueFromSchema = (
1060
- schema: S.Codec<any>,
1061
- record: Record<string, any> = {}
1062
- ): any => {
1063
- const ast = schema.ast
1064
-
1065
- if (isNullableOrUndefined(schema.ast) === "null") {
1066
- return null
1067
- }
1068
- if (isNullableOrUndefined(schema.ast) === "undefined") {
1069
- return undefined
1070
- }
1071
-
1072
- // Handle v4 Objects AST structure
1073
- if (AST.isObjects(ast)) {
1074
- const result: Record<string, any> = { ...record }
1075
-
1076
- for (const prop of ast.propertySignatures) {
1077
- const key = prop.name.toString()
1078
- const propType = prop.type
1079
-
1080
- // Get the property schema from the original schema's fields if available
1081
- // This preserves schema wrappers like withDefaultConstructor
1082
- let propSchema: S.Codec<any>
1083
- if ((schema as any).fields && (schema as any).fields[key]) {
1084
- propSchema = (schema as any).fields[key]
1085
- } else {
1086
- propSchema = S.make(propType)
1087
- }
1088
-
1089
- // Recursively process the property to get its defaults
1090
- const propValue = defaultsValueFromSchema(propSchema, record[key] || {})
1091
-
1092
- if (propValue !== undefined) {
1093
- result[key] = propValue
1094
- }
1095
- }
1096
-
1097
- return result
1098
- }
1099
-
1100
- // v3 compatible fields extraction
1101
- if (hasFields(schema)) {
1102
- // Process fields and extract default values
1103
- const result: Record<string, any> = {}
1104
-
1105
- for (const [key, fieldSchema] of Object.entries(schema.fields)) {
1106
- // Check if this field has a defaultValue in its AST
1107
- const fieldAst = (fieldSchema as any)?.ast
1108
- if (fieldAst?.defaultValue) {
1109
- try {
1110
- result[key] = fieldAst.defaultValue()
1111
- continue
1112
- } catch {
1113
- // If defaultValue() throws, fall through to recursive processing
1114
- }
1115
- }
1116
-
1117
- // Recursively process the field
1118
- const fieldValue = defaultsValueFromSchema(fieldSchema as any, record[key] || {})
1119
- if (fieldValue !== undefined) {
1120
- result[key] = fieldValue
1121
- }
1122
- }
1123
-
1124
- return { ...result, ...record }
1125
- }
1126
-
1127
- // Check if schema has fields in from (for ExtendedClass and similar transformations)
1128
- if ((schema as any)?.from && hasFields((schema as any).from)) {
1129
- return defaultsValueFromSchema((schema as any).from, record)
1130
- }
1131
-
1132
- if (hasMembers(schema)) {
1133
- // Merge all member fields, giving precedence to fields with default values
1134
- const mergedMembers = schema.members.reduce((acc, member) => {
1135
- if (hasFields(member)) {
1136
- // Check each field and give precedence to ones with default values
1137
- Object.entries(member.fields).forEach(([key, fieldSchema]) => {
1138
- const fieldAst: any = fieldSchema.ast
1139
- const existingFieldAst: any = acc[key]?.ast
1140
-
1141
- // If field doesn't exist yet, or new field has default and existing doesn't, use new field
1142
- if (!acc[key] || (fieldAst?.defaultValue && !existingFieldAst?.defaultValue)) {
1143
- acc[key] = fieldSchema
1144
- }
1145
- // If both have defaults or neither have defaults, keep the first one (existing)
1146
- })
1147
- return acc
1148
- }
1149
- return acc
1150
- }, {} as Record<string, any>)
1151
-
1152
- // Use reduce to properly accumulate the merged fields
1153
- return Object.entries(mergedMembers).reduce((acc, [key, value]) => {
1154
- acc[key] = defaultsValueFromSchema(value, record[key] || {})
1155
- return acc
1156
- }, record)
1157
- }
1158
-
1159
- if (Object.keys(record).length === 0) {
1160
- // Check for constructor defaults in v4's context
1161
- if (ast.context?.defaultValue) {
1162
- // In v4, defaultValue is an Encoding type, not directly callable
1163
- // For now, skip complex default extraction
1164
- // TODO: properly extract default from encoding chain
1165
- }
1166
- }
1167
-
1168
- if (AST.isString(ast)) {
1169
- return ""
1170
- }
1171
- if (AST.isBoolean(ast)) {
1172
- return false
1173
- }
1174
- }