@effect-app/vue-components 4.0.0-beta.20 → 4.0.0-beta.201

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