@effect-app/vue-components 4.0.0-beta.15 → 4.0.0-beta.151
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.
- package/README.md +24 -0
- package/dist/reset.css +52 -0
- package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +24 -8
- package/dist/types/components/OmegaForm/useOmegaForm.d.ts +1 -1
- package/dist/types/utils/index.d.ts +6 -6
- package/dist/vue-components.es.js +21 -45
- package/dist/vue-components10.es.js +5 -0
- package/dist/vue-components11.es.js +13 -0
- package/dist/vue-components12.es.js +471 -0
- package/dist/vue-components13.es.js +4 -0
- package/dist/vue-components14.es.js +38 -0
- package/dist/vue-components15.es.js +27 -0
- package/dist/vue-components16.es.js +28 -0
- package/dist/vue-components17.es.js +7 -0
- package/dist/vue-components18.es.js +18 -0
- package/dist/vue-components19.es.js +36 -0
- package/dist/vue-components2.es.js +11 -0
- package/dist/vue-components20.es.js +18 -0
- package/dist/vue-components21.es.js +21 -0
- package/dist/vue-components22.es.js +30 -0
- package/dist/vue-components23.es.js +7 -0
- package/dist/vue-components24.es.js +9 -0
- package/dist/vue-components25.es.js +38 -0
- package/dist/vue-components26.es.js +25 -0
- package/dist/vue-components27.es.js +128 -0
- package/dist/vue-components28.es.js +24 -0
- package/dist/vue-components29.es.js +21 -0
- package/dist/vue-components3.es.js +54 -0
- package/dist/vue-components30.es.js +9 -0
- package/dist/vue-components31.es.js +19 -0
- package/dist/vue-components32.es.js +5 -0
- package/dist/vue-components33.es.js +29 -0
- package/dist/vue-components34.es.js +5 -0
- package/dist/vue-components35.es.js +29 -0
- package/dist/vue-components36.es.js +6 -0
- package/dist/vue-components37.es.js +18 -0
- package/dist/vue-components38.es.js +56 -0
- package/dist/vue-components39.es.js +5 -0
- package/dist/vue-components4.es.js +5 -0
- package/dist/vue-components40.es.js +44 -0
- package/dist/vue-components41.es.js +5 -0
- package/dist/vue-components42.es.js +84 -0
- package/dist/vue-components44.es.js +8 -0
- package/dist/vue-components45.es.js +9 -0
- package/dist/vue-components46.es.js +269 -0
- package/dist/vue-components48.es.js +8 -0
- package/dist/vue-components49.es.js +80 -0
- package/dist/vue-components5.es.js +24 -0
- package/dist/vue-components50.es.js +5 -0
- package/dist/vue-components51.es.js +73 -0
- package/dist/vue-components52.es.js +5 -0
- package/dist/vue-components53.es.js +24 -0
- package/dist/vue-components54.es.js +5 -0
- package/dist/vue-components55.es.js +59 -0
- package/dist/vue-components56.es.js +5 -0
- package/dist/vue-components57.es.js +12 -0
- package/dist/vue-components58.es.js +22 -0
- package/dist/vue-components6.es.js +13 -0
- package/dist/vue-components60.es.js +9 -0
- package/dist/vue-components61.es.js +235 -0
- package/dist/vue-components62.es.js +33 -0
- package/dist/vue-components63.es.js +8 -0
- package/dist/vue-components64.es.js +36 -0
- package/dist/vue-components7.es.js +28 -0
- package/dist/vue-components8.es.js +47 -0
- package/dist/vue-components9.es.js +5 -0
- package/package.json +33 -28
- package/src/components/CommandButton.vue +1 -1
- package/src/components/OmegaForm/OmegaFormStuff.ts +367 -129
- package/src/components/OmegaForm/OmegaInput.vue +36 -2
- package/src/components/OmegaForm/OmegaInputVuetify.vue +4 -1
- package/src/components/OmegaForm/OmegaInternalInput.vue +19 -5
- package/src/components/OmegaForm/useOmegaForm.ts +31 -8
- package/src/reset.css +52 -0
- package/src/utils/index.ts +10 -7
- package/dist/vue-components.es10.js +0 -239
- package/dist/vue-components.es11.js +0 -32
- package/dist/vue-components.es12.js +0 -481
- package/dist/vue-components.es13.js +0 -49
- package/dist/vue-components.es14.js +0 -4
- package/dist/vue-components.es15.js +0 -4
- package/dist/vue-components.es16.js +0 -13
- package/dist/vue-components.es17.js +0 -6
- package/dist/vue-components.es18.js +0 -13
- package/dist/vue-components.es19.js +0 -57
- package/dist/vue-components.es2.js +0 -31
- package/dist/vue-components.es20.js +0 -56
- package/dist/vue-components.es21.js +0 -8
- package/dist/vue-components.es22.js +0 -8
- package/dist/vue-components.es23.js +0 -5
- package/dist/vue-components.es24.js +0 -5
- package/dist/vue-components.es25.js +0 -4
- package/dist/vue-components.es26.js +0 -4
- package/dist/vue-components.es27.js +0 -4
- package/dist/vue-components.es28.js +0 -4
- package/dist/vue-components.es29.js +0 -19
- package/dist/vue-components.es3.js +0 -17
- package/dist/vue-components.es30.js +0 -31
- package/dist/vue-components.es31.js +0 -6
- package/dist/vue-components.es32.js +0 -4
- package/dist/vue-components.es33.js +0 -4
- package/dist/vue-components.es34.js +0 -113
- package/dist/vue-components.es36.js +0 -9
- package/dist/vue-components.es37.js +0 -34
- package/dist/vue-components.es39.js +0 -194
- package/dist/vue-components.es4.js +0 -52
- package/dist/vue-components.es41.js +0 -6
- package/dist/vue-components.es42.js +0 -25
- package/dist/vue-components.es43.js +0 -7
- package/dist/vue-components.es44.js +0 -23
- package/dist/vue-components.es45.js +0 -32
- package/dist/vue-components.es46.js +0 -24
- package/dist/vue-components.es47.js +0 -14
- package/dist/vue-components.es48.js +0 -7
- package/dist/vue-components.es49.js +0 -21
- package/dist/vue-components.es5.js +0 -52
- package/dist/vue-components.es50.js +0 -11
- package/dist/vue-components.es51.js +0 -33
- package/dist/vue-components.es52.js +0 -50
- package/dist/vue-components.es53.js +0 -28
- package/dist/vue-components.es54.js +0 -13
- package/dist/vue-components.es55.js +0 -67
- package/dist/vue-components.es56.js +0 -58
- package/dist/vue-components.es57.js +0 -19
- package/dist/vue-components.es58.js +0 -35
- package/dist/vue-components.es59.js +0 -31
- package/dist/vue-components.es6.js +0 -69
- package/dist/vue-components.es60.js +0 -44
- package/dist/vue-components.es61.js +0 -4
- package/dist/vue-components.es62.js +0 -46
- package/dist/vue-components.es63.js +0 -4
- package/dist/vue-components.es7.js +0 -83
- package/dist/vue-components.es8.js +0 -63
- package/dist/vue-components.es9.js +0 -21
|
@@ -2,14 +2,29 @@ import { Effect, Option, type Record, S } from "effect-app"
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
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
4
|
import { isObject } from "@vueuse/core"
|
|
5
|
-
import type
|
|
5
|
+
import type { Fiber as EffectFiber } from "effect/Fiber"
|
|
6
|
+
import type { Redacted } from "effect/Redacted"
|
|
6
7
|
import { getTransformationFrom, useIntl } from "../../utils"
|
|
7
8
|
import { type OmegaFieldInternalApi } from "./InputProps"
|
|
8
9
|
import { type OF, type OmegaFormReturn } from "./useOmegaForm"
|
|
9
10
|
|
|
11
|
+
const legacyTagWarningEmittedFor = new Set<string>()
|
|
12
|
+
type GlobalThisWithOptionalProcess = typeof globalThis & {
|
|
13
|
+
process?: {
|
|
14
|
+
env?: {
|
|
15
|
+
NODE_ENV?: string
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const isDevelopmentEnvironment = () => {
|
|
21
|
+
const process = (globalThis as GlobalThisWithOptionalProcess).process
|
|
22
|
+
return process?.env?.NODE_ENV !== "production"
|
|
23
|
+
}
|
|
24
|
+
|
|
10
25
|
export type FieldPath<T> = unknown extends T ? string
|
|
11
26
|
// technically we cannot have primitive at the root
|
|
12
|
-
: T extends string | boolean | number | null | undefined | symbol | bigint ? ""
|
|
27
|
+
: T extends string | boolean | number | null | undefined | symbol | bigint | Redacted<any> ? ""
|
|
13
28
|
// technically we cannot have array at the root
|
|
14
29
|
: T extends ReadonlyArray<infer U> ? FieldPath_<U, `[${number}]`>
|
|
15
30
|
: {
|
|
@@ -17,7 +32,7 @@ export type FieldPath<T> = unknown extends T ? string
|
|
|
17
32
|
}[keyof T]
|
|
18
33
|
|
|
19
34
|
export type FieldPath_<T, Path extends string> = unknown extends T ? string
|
|
20
|
-
: T extends string | boolean | number | null | undefined | symbol | bigint ? Path
|
|
35
|
+
: T extends string | boolean | number | null | undefined | symbol | bigint | Redacted<any> ? Path
|
|
21
36
|
: T extends ReadonlyArray<infer U> ? FieldPath_<U, `${Path}[${number}]`> | Path
|
|
22
37
|
: {
|
|
23
38
|
[K in keyof T]: FieldPath_<T[K], `${Path}.${K & string}`>
|
|
@@ -144,7 +159,7 @@ export type FormProps<From, To> =
|
|
|
144
159
|
formApi: OmegaFormParams<From, To>
|
|
145
160
|
meta: any
|
|
146
161
|
value: To
|
|
147
|
-
}) => Promise<any> |
|
|
162
|
+
}) => Promise<any> | EffectFiber<any, any> | Effect.Effect<unknown, any, never>
|
|
148
163
|
}
|
|
149
164
|
|
|
150
165
|
export type OmegaFormParams<From, To> = FormApi<
|
|
@@ -222,7 +237,13 @@ export type PrefixFromDepth<
|
|
|
222
237
|
_TDepth extends any[]
|
|
223
238
|
> = K
|
|
224
239
|
|
|
225
|
-
|
|
240
|
+
// Recursively replace Redacted<A> with its inner type so DeepKeys treats it as a leaf
|
|
241
|
+
type StripRedacted<T> = T extends Redacted<any> ? string
|
|
242
|
+
: T extends ReadonlyArray<infer U> ? ReadonlyArray<StripRedacted<U>>
|
|
243
|
+
: T extends Record<string, any> ? { [K in keyof T]: StripRedacted<T[K]> }
|
|
244
|
+
: T
|
|
245
|
+
|
|
246
|
+
export type NestedKeyOf<T> = DeepKeys<StripRedacted<T>>
|
|
226
247
|
|
|
227
248
|
export type FieldValidators<T> = {
|
|
228
249
|
onChangeAsync?: FieldAsyncValidateOrFn<T, any, any>
|
|
@@ -235,6 +256,15 @@ export type FieldValidators<T> = {
|
|
|
235
256
|
export type BaseFieldMeta = {
|
|
236
257
|
required: boolean
|
|
237
258
|
nullableOrUndefined?: false | "undefined" | "null"
|
|
259
|
+
originalSchema?: StandardSchemaV1<any, any>
|
|
260
|
+
/**
|
|
261
|
+
* True when the schema property is `S.optionalKey` (AST
|
|
262
|
+
* `context.isOptional`) — i.e. the key should be ABSENT from the submitted
|
|
263
|
+
* object when empty, not present with `undefined`. Distinct from
|
|
264
|
+
* `required: false`, which may also mean "empty string is valid" for
|
|
265
|
+
* unconstrained `S.String` fields.
|
|
266
|
+
*/
|
|
267
|
+
isOptionalKey?: boolean
|
|
238
268
|
}
|
|
239
269
|
|
|
240
270
|
export type StringFieldMeta = BaseFieldMeta & {
|
|
@@ -268,6 +298,10 @@ export type BooleanFieldMeta = BaseFieldMeta & {
|
|
|
268
298
|
type: "boolean"
|
|
269
299
|
}
|
|
270
300
|
|
|
301
|
+
export type DateFieldMeta = BaseFieldMeta & {
|
|
302
|
+
type: "date"
|
|
303
|
+
}
|
|
304
|
+
|
|
271
305
|
export type UnknownFieldMeta = BaseFieldMeta & {
|
|
272
306
|
type: "unknown"
|
|
273
307
|
}
|
|
@@ -278,6 +312,7 @@ export type FieldMeta =
|
|
|
278
312
|
| SelectFieldMeta
|
|
279
313
|
| MultipleFieldMeta
|
|
280
314
|
| BooleanFieldMeta
|
|
315
|
+
| DateFieldMeta
|
|
281
316
|
| UnknownFieldMeta
|
|
282
317
|
|
|
283
318
|
export type MetaRecord<T = string> = {
|
|
@@ -321,6 +356,17 @@ const unwrapDeclaration = (property: S.AST.AST): S.AST.AST => {
|
|
|
321
356
|
|
|
322
357
|
const isNullishType = (property: S.AST.AST) => S.AST.isUndefined(property) || S.AST.isNull(property)
|
|
323
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Unwrap a single-element Union to its inner type if it's a Literal.
|
|
361
|
+
* After AST.toType, S.Struct({ _tag: S.Literal("X") }) produces Union([Literal("X")])
|
|
362
|
+
* instead of bare Literal("X") like S.TaggedStruct does.
|
|
363
|
+
* TODO: remove after manual _tag deprecation
|
|
364
|
+
*/
|
|
365
|
+
const unwrapSingleLiteralUnion = (ast: S.AST.AST): S.AST.AST =>
|
|
366
|
+
S.AST.isUnion(ast) && ast.types.length === 1 && S.AST.isLiteral(ast.types[0]!)
|
|
367
|
+
? ast.types[0]!
|
|
368
|
+
: ast
|
|
369
|
+
|
|
324
370
|
const getNullableOrUndefined = (property: S.AST.AST) =>
|
|
325
371
|
S.AST.isUnion(property)
|
|
326
372
|
? property.types.find((_) => isNullishType(_))
|
|
@@ -360,13 +406,8 @@ const getJsonSchemaAnnotation = (property: S.AST.AST): Record<string, unknown> =
|
|
|
360
406
|
return jsonSchema && typeof jsonSchema === "object" ? jsonSchema as Record<string, unknown> : {}
|
|
361
407
|
}
|
|
362
408
|
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (!link?.transformation?.decode?.run) {
|
|
367
|
-
return undefined
|
|
368
|
-
}
|
|
369
|
-
|
|
409
|
+
const extractDefaultFromLink = (link: any): unknown | undefined => {
|
|
410
|
+
if (!link?.transformation?.decode?.run) return undefined
|
|
370
411
|
try {
|
|
371
412
|
const result = Effect.runSync(link.transformation.decode.run(Option.none())) as Option.Option<unknown>
|
|
372
413
|
return Option.isSome(result) ? result.value : undefined
|
|
@@ -375,6 +416,21 @@ const getDefaultFromAst = (property: S.AST.AST) => {
|
|
|
375
416
|
}
|
|
376
417
|
}
|
|
377
418
|
|
|
419
|
+
const getDefaultFromAst = (property: S.AST.AST) => {
|
|
420
|
+
// 1. Check withConstructorDefault (stored in context.defaultValue)
|
|
421
|
+
const constructorLink = property.context?.defaultValue?.[0]
|
|
422
|
+
const constructorDefault = extractDefaultFromLink(constructorLink)
|
|
423
|
+
if (constructorDefault !== undefined) return constructorDefault
|
|
424
|
+
|
|
425
|
+
// 2. Check withDecodingDefault (stored in encoding)
|
|
426
|
+
const encodingLink = property.encoding?.[0]
|
|
427
|
+
if (encodingLink && property.context?.isOptional) {
|
|
428
|
+
return extractDefaultFromLink(encodingLink)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return undefined
|
|
432
|
+
}
|
|
433
|
+
|
|
378
434
|
const getCheckMetas = (property: S.AST.AST): Array<Record<string, any>> => {
|
|
379
435
|
const checks = property.checks ?? []
|
|
380
436
|
|
|
@@ -426,6 +482,10 @@ const getFieldMetadataFromAst = (property: S.AST.AST) => {
|
|
|
426
482
|
case "isLessThanOrEqualTo":
|
|
427
483
|
base.maximum = check.maximum
|
|
428
484
|
break
|
|
485
|
+
case "isBetween":
|
|
486
|
+
base.minimum = check.minimum
|
|
487
|
+
base.maximum = check.maximum
|
|
488
|
+
break
|
|
429
489
|
case "isGreaterThan":
|
|
430
490
|
base.exclusiveMinimum = check.exclusiveMinimum
|
|
431
491
|
break
|
|
@@ -436,6 +496,11 @@ const getFieldMetadataFromAst = (property: S.AST.AST) => {
|
|
|
436
496
|
}
|
|
437
497
|
} else if (S.AST.isBoolean(property)) {
|
|
438
498
|
base.type = "boolean"
|
|
499
|
+
} else if (
|
|
500
|
+
S.AST.isDeclaration(property)
|
|
501
|
+
&& (property.annotations as any)?.typeConstructor?._tag === "Date"
|
|
502
|
+
) {
|
|
503
|
+
base.type = "date"
|
|
439
504
|
} else {
|
|
440
505
|
base.type = "unknown"
|
|
441
506
|
}
|
|
@@ -445,7 +510,8 @@ const getFieldMetadataFromAst = (property: S.AST.AST) => {
|
|
|
445
510
|
|
|
446
511
|
export const createMeta = <T = any>(
|
|
447
512
|
{ meta = {}, parent = "", property, propertySignatures }: CreateMeta,
|
|
448
|
-
acc: Partial<MetaRecord<T>> = {}
|
|
513
|
+
acc: Partial<MetaRecord<T>> = {},
|
|
514
|
+
fieldAstByPath?: Record<string, S.AST.AST>
|
|
449
515
|
): MetaRecord<T> | FieldMeta => {
|
|
450
516
|
if (property) {
|
|
451
517
|
property = unwrapDeclaration(property)
|
|
@@ -463,8 +529,11 @@ export const createMeta = <T = any>(
|
|
|
463
529
|
const key = parent ? `${parent}.${p.name.toString()}` : p.name.toString()
|
|
464
530
|
const nullableOrUndefined = isNullableOrUndefined(p.type)
|
|
465
531
|
|
|
532
|
+
const isOptionalKey = (p.type as any).context?.isOptional === true
|
|
533
|
+
|
|
466
534
|
// Determine if this field should be required:
|
|
467
535
|
// - For nullable discriminated unions, only _tag should be non-required
|
|
536
|
+
// - optionalKey fields are not required
|
|
468
537
|
// - All other fields should calculate their required status normally
|
|
469
538
|
let isRequired: boolean
|
|
470
539
|
if (meta._isNullableDiscriminatedUnion && p.name.toString() === "_tag") {
|
|
@@ -473,6 +542,8 @@ export const createMeta = <T = any>(
|
|
|
473
542
|
} else if (meta.required === false) {
|
|
474
543
|
// Explicitly set to non-required (legacy behavior for backwards compatibility)
|
|
475
544
|
isRequired = false
|
|
545
|
+
} else if (isOptionalKey) {
|
|
546
|
+
isRequired = false
|
|
476
547
|
} else {
|
|
477
548
|
// Calculate from the property itself
|
|
478
549
|
isRequired = !nullableOrUndefined
|
|
@@ -503,14 +574,28 @@ export const createMeta = <T = any>(
|
|
|
503
574
|
// - All other fields maintain their normal required status based on their own types
|
|
504
575
|
const isNullableDiscriminatedUnion = nullableOrUndefined && nonNullTypes.length > 1
|
|
505
576
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
)
|
|
577
|
+
const branchMeta = createMeta<T>({
|
|
578
|
+
parent: key,
|
|
579
|
+
propertySignatures: nonNullType.propertySignatures,
|
|
580
|
+
meta: isNullableDiscriminatedUnion ? { _isNullableDiscriminatedUnion: true } : {}
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
// Merge branch metadata, combining select members for shared discriminator fields
|
|
584
|
+
for (const [metaKey, metaValue] of Object.entries(branchMeta)) {
|
|
585
|
+
const existing = acc[metaKey as NestedKeyOf<T>] as FieldMeta | undefined
|
|
586
|
+
if (
|
|
587
|
+
existing && existing.type === "select" && (metaValue as any)?.type === "select"
|
|
588
|
+
) {
|
|
589
|
+
existing.members = [
|
|
590
|
+
...existing.members,
|
|
591
|
+
...(metaValue as SelectFieldMeta).members.filter(
|
|
592
|
+
(m: any) => !existing.members.includes(m)
|
|
593
|
+
)
|
|
594
|
+
]
|
|
595
|
+
} else {
|
|
596
|
+
acc[metaKey as NestedKeyOf<T>] = metaValue as FieldMeta
|
|
597
|
+
}
|
|
598
|
+
}
|
|
514
599
|
}
|
|
515
600
|
}
|
|
516
601
|
} else {
|
|
@@ -525,6 +610,9 @@ export const createMeta = <T = any>(
|
|
|
525
610
|
required: isRequired,
|
|
526
611
|
nullableOrUndefined
|
|
527
612
|
} as FieldMeta
|
|
613
|
+
if (fieldAstByPath) {
|
|
614
|
+
fieldAstByPath[key] = p.type
|
|
615
|
+
}
|
|
528
616
|
|
|
529
617
|
// If the array has struct elements, also create metadata for their properties
|
|
530
618
|
if (arrayType.rest && arrayType.rest.length > 0) {
|
|
@@ -545,6 +633,9 @@ export const createMeta = <T = any>(
|
|
|
545
633
|
// add to accumulator if valid
|
|
546
634
|
if (propMeta && typeof propMeta === "object" && "type" in propMeta) {
|
|
547
635
|
acc[propKey as NestedKeyOf<T>] = propMeta as FieldMeta
|
|
636
|
+
if (fieldAstByPath) {
|
|
637
|
+
fieldAstByPath[propKey] = prop.type
|
|
638
|
+
}
|
|
548
639
|
|
|
549
640
|
if (
|
|
550
641
|
propMeta.type === "multiple" && S.AST.isArrays(prop.type) && prop
|
|
@@ -568,6 +659,9 @@ export const createMeta = <T = any>(
|
|
|
568
659
|
// add to accumulator if valid
|
|
569
660
|
if (nestedPropMeta && typeof nestedPropMeta === "object" && "type" in nestedPropMeta) {
|
|
570
661
|
acc[nestedPropKey as NestedKeyOf<T>] = nestedPropMeta as FieldMeta
|
|
662
|
+
if (fieldAstByPath) {
|
|
663
|
+
fieldAstByPath[nestedPropKey] = nestedProp.type
|
|
664
|
+
}
|
|
571
665
|
}
|
|
572
666
|
}
|
|
573
667
|
}
|
|
@@ -584,17 +678,24 @@ export const createMeta = <T = any>(
|
|
|
584
678
|
meta: { required: isRequired, nullableOrUndefined }
|
|
585
679
|
})
|
|
586
680
|
acc[key as NestedKeyOf<T>] = newMeta as FieldMeta
|
|
681
|
+
if (fieldAstByPath) {
|
|
682
|
+
fieldAstByPath[key] = p.type
|
|
683
|
+
}
|
|
587
684
|
}
|
|
588
685
|
}
|
|
589
686
|
} else {
|
|
590
687
|
if (S.AST.isObjects(typeToProcess)) {
|
|
591
688
|
Object.assign(
|
|
592
689
|
acc,
|
|
593
|
-
createMeta<T>(
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
690
|
+
createMeta<T>(
|
|
691
|
+
{
|
|
692
|
+
parent: key,
|
|
693
|
+
propertySignatures: typeToProcess.propertySignatures,
|
|
694
|
+
meta: { required: isRequired, nullableOrUndefined }
|
|
695
|
+
},
|
|
696
|
+
{},
|
|
697
|
+
fieldAstByPath
|
|
698
|
+
)
|
|
598
699
|
)
|
|
599
700
|
} else if (S.AST.isArrays(p.type)) {
|
|
600
701
|
// Check if it has struct elements
|
|
@@ -625,6 +726,9 @@ export const createMeta = <T = any>(
|
|
|
625
726
|
}
|
|
626
727
|
})
|
|
627
728
|
acc[nestedKey as NestedKeyOf<T>] = nestedMeta as FieldMeta
|
|
729
|
+
if (fieldAstByPath) {
|
|
730
|
+
fieldAstByPath[nestedKey] = nestedProp.type
|
|
731
|
+
}
|
|
628
732
|
}
|
|
629
733
|
} else {
|
|
630
734
|
// Array with primitive elements - create meta for the array itself
|
|
@@ -635,6 +739,9 @@ export const createMeta = <T = any>(
|
|
|
635
739
|
required: !isNullableOrUndefined(prop.type),
|
|
636
740
|
nullableOrUndefined: isNullableOrUndefined(prop.type)
|
|
637
741
|
} as FieldMeta
|
|
742
|
+
if (fieldAstByPath) {
|
|
743
|
+
fieldAstByPath[propKey] = prop.type
|
|
744
|
+
}
|
|
638
745
|
}
|
|
639
746
|
} else {
|
|
640
747
|
const fieldMeta = createMeta<T>({
|
|
@@ -646,6 +753,9 @@ export const createMeta = <T = any>(
|
|
|
646
753
|
}
|
|
647
754
|
})
|
|
648
755
|
acc[propKey as NestedKeyOf<T>] = fieldMeta as FieldMeta
|
|
756
|
+
if (fieldAstByPath) {
|
|
757
|
+
fieldAstByPath[propKey] = prop.type
|
|
758
|
+
}
|
|
649
759
|
}
|
|
650
760
|
}
|
|
651
761
|
}
|
|
@@ -658,6 +768,9 @@ export const createMeta = <T = any>(
|
|
|
658
768
|
required: isRequired,
|
|
659
769
|
nullableOrUndefined
|
|
660
770
|
} as FieldMeta
|
|
771
|
+
if (fieldAstByPath) {
|
|
772
|
+
fieldAstByPath[key] = p.type
|
|
773
|
+
}
|
|
661
774
|
}
|
|
662
775
|
} else {
|
|
663
776
|
const newMeta = createMeta<T>({
|
|
@@ -667,12 +780,16 @@ export const createMeta = <T = any>(
|
|
|
667
780
|
// an empty string is valid for a S.String field, so we should not mark it as required
|
|
668
781
|
// TODO: handle this better via the createMeta minLength parsing
|
|
669
782
|
required: isRequired
|
|
670
|
-
&& (!S.AST.isString(typeToProcess) || !!getFieldMetadataFromAst(
|
|
671
|
-
nullableOrUndefined
|
|
783
|
+
&& (!S.AST.isString(typeToProcess) || !!getFieldMetadataFromAst(typeToProcess).minLength),
|
|
784
|
+
nullableOrUndefined,
|
|
785
|
+
...(isOptionalKey ? { isOptionalKey: true } : {})
|
|
672
786
|
}
|
|
673
787
|
})
|
|
674
788
|
|
|
675
789
|
acc[key as NestedKeyOf<T>] = newMeta as FieldMeta
|
|
790
|
+
if (fieldAstByPath) {
|
|
791
|
+
fieldAstByPath[key] = p.type
|
|
792
|
+
}
|
|
676
793
|
}
|
|
677
794
|
}
|
|
678
795
|
}
|
|
@@ -689,7 +806,20 @@ export const createMeta = <T = any>(
|
|
|
689
806
|
|
|
690
807
|
if (S.AST.isUnion(property)) {
|
|
691
808
|
const unwrappedTypes = unwrapNestedUnions(property.types).map(unwrapDeclaration)
|
|
692
|
-
const
|
|
809
|
+
const nonNullTypes = unwrappedTypes.filter((t) => !isNullishType(t))
|
|
810
|
+
|
|
811
|
+
// Unwrap single-element unions when the literal is a boolean
|
|
812
|
+
// (effect-app's S.Literal wraps as S.Literals([x]) → Union([Literal(x)]))
|
|
813
|
+
// Don't unwrap string/number literals — they may be discriminator values in a union
|
|
814
|
+
if (
|
|
815
|
+
nonNullTypes.length === 1
|
|
816
|
+
&& S.AST.isLiteral(nonNullTypes[0]!)
|
|
817
|
+
&& typeof nonNullTypes[0]!.literal === "boolean"
|
|
818
|
+
) {
|
|
819
|
+
return createMeta<T>({ parent, meta, property: nonNullTypes[0]! })
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const nonNullType = nonNullTypes[0]!
|
|
693
823
|
|
|
694
824
|
if (S.AST.isObjects(nonNullType)) {
|
|
695
825
|
return createMeta<T>({
|
|
@@ -699,11 +829,13 @@ export const createMeta = <T = any>(
|
|
|
699
829
|
})
|
|
700
830
|
}
|
|
701
831
|
|
|
702
|
-
|
|
832
|
+
// TODO: remove after manual _tag deprecation — unwrap legacy S.Struct({ _tag: S.Literal("X") }) pattern
|
|
833
|
+
const resolvedTypes = unwrappedTypes.map(unwrapSingleLiteralUnion)
|
|
834
|
+
if (resolvedTypes.every((_) => isNullishType(_) || S.AST.isLiteral(_))) {
|
|
703
835
|
return {
|
|
704
836
|
...meta,
|
|
705
837
|
type: "select",
|
|
706
|
-
members:
|
|
838
|
+
members: resolvedTypes.filter(S.AST.isLiteral).map((t) => t.literal)
|
|
707
839
|
} as FieldMeta
|
|
708
840
|
}
|
|
709
841
|
|
|
@@ -726,6 +858,14 @@ export const createMeta = <T = any>(
|
|
|
726
858
|
} as FieldMeta
|
|
727
859
|
}
|
|
728
860
|
|
|
861
|
+
if (S.AST.isLiteral(property)) {
|
|
862
|
+
return {
|
|
863
|
+
...meta,
|
|
864
|
+
type: "select",
|
|
865
|
+
members: [property.literal]
|
|
866
|
+
} as FieldMeta
|
|
867
|
+
}
|
|
868
|
+
|
|
729
869
|
meta = { ...getJsonSchemaAnnotation(property), ...getFieldMetadataFromAst(property), ...meta }
|
|
730
870
|
|
|
731
871
|
return meta as FieldMeta
|
|
@@ -754,11 +894,50 @@ const flattenMeta = <T>(meta: MetaRecord<T> | FieldMeta, parentKey: string = "")
|
|
|
754
894
|
|
|
755
895
|
const metadataFromAst = <From, To>(
|
|
756
896
|
schema: S.Codec<To, From, never>
|
|
757
|
-
): {
|
|
897
|
+
): {
|
|
898
|
+
meta: MetaRecord<To>
|
|
899
|
+
defaultValues: Record<string, any>
|
|
900
|
+
unionMeta: Record<string, MetaRecord<To>>
|
|
901
|
+
} => {
|
|
758
902
|
const ast = unwrapDeclaration(schema.ast)
|
|
759
903
|
const newMeta: MetaRecord<To> = {}
|
|
760
904
|
const defaultValues: Record<string, any> = {}
|
|
761
905
|
const unionMeta: Record<string, MetaRecord<To>> = {}
|
|
906
|
+
const fieldAstByPath: Record<string, S.AST.AST> = {}
|
|
907
|
+
|
|
908
|
+
const toFieldStandardSchema = (
|
|
909
|
+
propertyAst: S.AST.AST,
|
|
910
|
+
required: boolean
|
|
911
|
+
): StandardSchemaV1<any, any> => {
|
|
912
|
+
const base = S.make(propertyAst)
|
|
913
|
+
const fieldSchema = required ? base : S.NullishOr(base)
|
|
914
|
+
return S.toStandardSchemaV1(fieldSchema as any)
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const attachOriginalSchemas = (metaRecord: MetaRecord<To>) => {
|
|
918
|
+
for (const [key, fieldAst] of Object.entries(fieldAstByPath)) {
|
|
919
|
+
const fieldMeta = metaRecord[key as NestedKeyOf<To>]
|
|
920
|
+
if (!fieldMeta) {
|
|
921
|
+
continue
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
const required = fieldMeta.required ?? true
|
|
925
|
+
Object.defineProperty(fieldMeta, "originalSchema", {
|
|
926
|
+
value: toFieldStandardSchema(fieldAst, required),
|
|
927
|
+
enumerable: false,
|
|
928
|
+
configurable: true,
|
|
929
|
+
writable: true
|
|
930
|
+
})
|
|
931
|
+
} catch {
|
|
932
|
+
Object.defineProperty(fieldMeta, "originalSchema", {
|
|
933
|
+
value: S.toStandardSchemaV1(S.Unknown),
|
|
934
|
+
enumerable: false,
|
|
935
|
+
configurable: true,
|
|
936
|
+
writable: true
|
|
937
|
+
})
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
762
941
|
|
|
763
942
|
// Handle root-level Union types (discriminated unions)
|
|
764
943
|
if (S.AST.isUnion(ast)) {
|
|
@@ -781,15 +960,35 @@ const metadataFromAst = <From, To>(
|
|
|
781
960
|
)
|
|
782
961
|
|
|
783
962
|
let tagValue: string | null = null
|
|
784
|
-
|
|
785
|
-
|
|
963
|
+
// TODO: remove after manual _tag deprecation — unwrap legacy S.Struct({ _tag: S.Literal("X") }) pattern
|
|
964
|
+
const resolvedTagType = tagProp ? unwrapSingleLiteralUnion(tagProp.type) : null
|
|
965
|
+
if (resolvedTagType && S.AST.isLiteral(resolvedTagType)) {
|
|
966
|
+
tagValue = resolvedTagType.literal as string
|
|
786
967
|
discriminatorValues.push(tagValue)
|
|
968
|
+
// Warn if the tag was wrapped in a single-element Union (legacy pattern)
|
|
969
|
+
if (
|
|
970
|
+
tagProp
|
|
971
|
+
&& S.AST.isUnion(tagProp.type)
|
|
972
|
+
&& isDevelopmentEnvironment()
|
|
973
|
+
&& tagValue != null
|
|
974
|
+
&& !legacyTagWarningEmittedFor.has(tagValue)
|
|
975
|
+
) {
|
|
976
|
+
legacyTagWarningEmittedFor.add(tagValue)
|
|
977
|
+
console.warn(
|
|
978
|
+
`[OmegaForm] Union member with _tag "${tagValue}" uses S.Struct({ _tag: S.Literal("${tagValue}"), ... }). `
|
|
979
|
+
+ `Please migrate to S.TaggedStruct("${tagValue}", { ... }) for cleaner AST handling.`
|
|
980
|
+
)
|
|
981
|
+
}
|
|
787
982
|
}
|
|
788
983
|
|
|
789
984
|
// Create metadata for this member's properties
|
|
790
|
-
const memberMeta = createMeta<To>(
|
|
791
|
-
|
|
792
|
-
|
|
985
|
+
const memberMeta = createMeta<To>(
|
|
986
|
+
{
|
|
987
|
+
propertySignatures: memberType.propertySignatures
|
|
988
|
+
},
|
|
989
|
+
{},
|
|
990
|
+
fieldAstByPath
|
|
991
|
+
)
|
|
793
992
|
|
|
794
993
|
// Store per-tag metadata for reactive lookup
|
|
795
994
|
if (tagValue) {
|
|
@@ -810,17 +1009,28 @@ const metadataFromAst = <From, To>(
|
|
|
810
1009
|
} as FieldMeta
|
|
811
1010
|
}
|
|
812
1011
|
|
|
1012
|
+
attachOriginalSchemas(newMeta)
|
|
813
1013
|
return { meta: newMeta, defaultValues, unionMeta }
|
|
814
1014
|
}
|
|
815
1015
|
}
|
|
816
1016
|
|
|
817
1017
|
if (S.AST.isObjects(ast)) {
|
|
818
|
-
const meta = createMeta<To>(
|
|
819
|
-
|
|
820
|
-
|
|
1018
|
+
const meta = createMeta<To>(
|
|
1019
|
+
{
|
|
1020
|
+
propertySignatures: ast.propertySignatures
|
|
1021
|
+
},
|
|
1022
|
+
{},
|
|
1023
|
+
fieldAstByPath
|
|
1024
|
+
)
|
|
821
1025
|
|
|
822
1026
|
if (Object.values(meta).every((value) => value && "type" in value)) {
|
|
823
|
-
|
|
1027
|
+
const typedMeta = meta as MetaRecord<To>
|
|
1028
|
+
attachOriginalSchemas(typedMeta)
|
|
1029
|
+
return {
|
|
1030
|
+
meta: typedMeta,
|
|
1031
|
+
defaultValues,
|
|
1032
|
+
unionMeta
|
|
1033
|
+
}
|
|
824
1034
|
}
|
|
825
1035
|
|
|
826
1036
|
const flattenObject = (
|
|
@@ -840,9 +1050,74 @@ const metadataFromAst = <From, To>(
|
|
|
840
1050
|
flattenObject(meta)
|
|
841
1051
|
}
|
|
842
1052
|
|
|
1053
|
+
attachOriginalSchemas(newMeta)
|
|
843
1054
|
return { meta: newMeta, defaultValues, unionMeta }
|
|
844
1055
|
}
|
|
845
1056
|
|
|
1057
|
+
/*
|
|
1058
|
+
* Checks if an AST node is a S.Redacted Declaration without encoding.
|
|
1059
|
+
* These need to be swapped to S.RedactedFromValue for form usage
|
|
1060
|
+
* because S.Redacted expects Redacted objects, not plain strings.
|
|
1061
|
+
*/
|
|
1062
|
+
const isRedactedWithoutEncoding = (ast: S.AST.AST): boolean =>
|
|
1063
|
+
S.AST.isDeclaration(ast)
|
|
1064
|
+
&& (ast.annotations as any)?.typeConstructor?._tag === "effect/Redacted"
|
|
1065
|
+
&& !ast.encoding
|
|
1066
|
+
|
|
1067
|
+
/*
|
|
1068
|
+
* Creates a form-compatible schema by replacing S.Redacted(X) with
|
|
1069
|
+
* S.RedactedFromValue(X). S.Redacted is a Declaration that expects
|
|
1070
|
+
* Redacted<A> on both encoded and type sides, so form inputs (which
|
|
1071
|
+
* produce plain strings) fail validation. S.RedactedFromValue accepts
|
|
1072
|
+
* plain values on the encoded side and wraps them in Redacted on decode.
|
|
1073
|
+
*/
|
|
1074
|
+
export const toFormSchema = <From, To>(
|
|
1075
|
+
schema: S.Codec<To, From, never>
|
|
1076
|
+
): S.Codec<To, From, never> => {
|
|
1077
|
+
const ast = schema.ast
|
|
1078
|
+
const objAst = S.AST.isObjects(ast)
|
|
1079
|
+
? ast
|
|
1080
|
+
: S.AST.isDeclaration(ast)
|
|
1081
|
+
? S.AST.toEncoded(ast)
|
|
1082
|
+
: null
|
|
1083
|
+
|
|
1084
|
+
if (!objAst || !("propertySignatures" in objAst)) return schema
|
|
1085
|
+
|
|
1086
|
+
let hasRedacted = false
|
|
1087
|
+
const props: Record<string, S.Struct.Fields[string]> = {}
|
|
1088
|
+
|
|
1089
|
+
for (const p of objAst.propertySignatures) {
|
|
1090
|
+
if (isRedactedWithoutEncoding(p.type)) {
|
|
1091
|
+
hasRedacted = true
|
|
1092
|
+
const innerSchema = S.make((p.type as S.AST.Declaration).typeParameters[0]!)
|
|
1093
|
+
props[p.name as string] = S.RedactedFromValue(innerSchema)
|
|
1094
|
+
} else if (S.AST.isUnion(p.type)) {
|
|
1095
|
+
const types = p.type.types
|
|
1096
|
+
const redactedType = types.find(isRedactedWithoutEncoding)
|
|
1097
|
+
if (redactedType) {
|
|
1098
|
+
hasRedacted = true
|
|
1099
|
+
const innerSchema = S.make((redactedType as S.AST.Declaration).typeParameters[0]!)
|
|
1100
|
+
const hasNull = types.some(S.AST.isNull)
|
|
1101
|
+
const hasUndefined = types.some(S.AST.isUndefined)
|
|
1102
|
+
const base = S.RedactedFromValue(innerSchema)
|
|
1103
|
+
props[p.name as string] = hasNull && hasUndefined
|
|
1104
|
+
? S.NullishOr(base)
|
|
1105
|
+
: hasNull
|
|
1106
|
+
? S.NullOr(base)
|
|
1107
|
+
: hasUndefined
|
|
1108
|
+
? S.UndefinedOr(base)
|
|
1109
|
+
: base
|
|
1110
|
+
} else {
|
|
1111
|
+
props[p.name as string] = S.make(p.type)
|
|
1112
|
+
}
|
|
1113
|
+
} else {
|
|
1114
|
+
props[p.name as string] = S.make(p.type)
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return hasRedacted ? S.Struct(props) as unknown as S.Codec<To, From, never> : schema
|
|
1119
|
+
}
|
|
1120
|
+
|
|
846
1121
|
export const duplicateSchema = <From, To>(
|
|
847
1122
|
schema: S.Codec<To, From, never>
|
|
848
1123
|
) => {
|
|
@@ -912,7 +1187,7 @@ export const generateInputStandardSchemaFromFieldMeta = (
|
|
|
912
1187
|
message: trans("validation.integer.expected", { actualValue: "NaN" })
|
|
913
1188
|
}))
|
|
914
1189
|
} else {
|
|
915
|
-
schema = S.
|
|
1190
|
+
schema = S.Finite.annotate({
|
|
916
1191
|
message: trans("validation.number.expected", { actualValue: "NaN" })
|
|
917
1192
|
})
|
|
918
1193
|
|
|
@@ -979,6 +1254,10 @@ export const generateInputStandardSchemaFromFieldMeta = (
|
|
|
979
1254
|
schema = S.Boolean
|
|
980
1255
|
break
|
|
981
1256
|
|
|
1257
|
+
case "date":
|
|
1258
|
+
schema = S.Date
|
|
1259
|
+
break
|
|
1260
|
+
|
|
982
1261
|
case "unknown":
|
|
983
1262
|
schema = S.Unknown
|
|
984
1263
|
break
|
|
@@ -996,11 +1275,6 @@ export const generateInputStandardSchemaFromFieldMeta = (
|
|
|
996
1275
|
return result
|
|
997
1276
|
}
|
|
998
1277
|
|
|
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
1278
|
export type OmegaAutoGenMeta<
|
|
1005
1279
|
From extends Record<PropertyKey, any>,
|
|
1006
1280
|
To extends Record<PropertyKey, any>,
|
|
@@ -1043,20 +1317,10 @@ export function deepMerge(target: any, source: any) {
|
|
|
1043
1317
|
return result
|
|
1044
1318
|
}
|
|
1045
1319
|
|
|
1046
|
-
// Type definitions for schemas with fields and members
|
|
1047
|
-
type SchemaWithFields = {
|
|
1048
|
-
fields: Record<string, S.Schema<any>>
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
1320
|
type SchemaWithMembers = {
|
|
1052
1321
|
members: readonly S.Schema<any>[]
|
|
1053
1322
|
}
|
|
1054
1323
|
|
|
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
1324
|
function hasMembers(schema: any): schema is SchemaWithMembers {
|
|
1061
1325
|
return schema && "members" in schema && Array.isArray(schema.members)
|
|
1062
1326
|
}
|
|
@@ -1080,99 +1344,73 @@ export const defaultsValueFromSchema = (
|
|
|
1080
1344
|
return undefined
|
|
1081
1345
|
}
|
|
1082
1346
|
|
|
1083
|
-
//
|
|
1084
|
-
|
|
1085
|
-
|
|
1347
|
+
// Handle structs via AST (covers plain structs, transformed schemas like decodeTo, Class, etc.)
|
|
1348
|
+
const objectsAst = S.AST.isObjects(ast)
|
|
1349
|
+
? ast
|
|
1350
|
+
: S.AST.isDeclaration(ast)
|
|
1351
|
+
? unwrapDeclaration(ast)
|
|
1352
|
+
: undefined
|
|
1353
|
+
if (objectsAst && S.AST.isObjects(objectsAst)) {
|
|
1086
1354
|
const result: Record<string, any> = {}
|
|
1087
1355
|
|
|
1088
|
-
for (const
|
|
1089
|
-
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
// If defaultValue() throws, fall through to recursive processing
|
|
1097
|
-
}
|
|
1356
|
+
for (const prop of objectsAst.propertySignatures) {
|
|
1357
|
+
const key = prop.name.toString()
|
|
1358
|
+
const propType = prop.type
|
|
1359
|
+
|
|
1360
|
+
const propDefault = getDefaultFromAst(propType)
|
|
1361
|
+
if (propDefault !== undefined) {
|
|
1362
|
+
result[key] = propDefault
|
|
1363
|
+
continue
|
|
1098
1364
|
}
|
|
1099
1365
|
|
|
1100
|
-
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1366
|
+
const propSchema = S.make(propType)
|
|
1367
|
+
const propValue = defaultsValueFromSchema(propSchema, record[key] || {})
|
|
1368
|
+
|
|
1369
|
+
if (propValue !== undefined) {
|
|
1370
|
+
result[key] = propValue
|
|
1371
|
+
} else if (isNullableOrUndefined(propType) === "undefined") {
|
|
1372
|
+
result[key] = undefined
|
|
1104
1373
|
}
|
|
1105
1374
|
}
|
|
1106
1375
|
|
|
1107
1376
|
return { ...result, ...record }
|
|
1108
1377
|
}
|
|
1109
1378
|
|
|
1110
|
-
//
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1379
|
+
// Handle unions via AST or schema-level .members
|
|
1380
|
+
const unionTypes = S.AST.isUnion(ast)
|
|
1381
|
+
? ast.types
|
|
1382
|
+
: hasMembers(schema)
|
|
1383
|
+
? schema.members.map((m) => m.ast)
|
|
1384
|
+
: undefined
|
|
1385
|
+
if (unionTypes) {
|
|
1386
|
+
const mergedFields: Record<string, { ast: S.AST.AST }> = {}
|
|
1114
1387
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
}
|
|
1128
|
-
// If both have defaults or neither have defaults, keep the first one (existing)
|
|
1129
|
-
})
|
|
1130
|
-
return acc
|
|
1388
|
+
for (const memberAstRaw of unionTypes) {
|
|
1389
|
+
const memberAst = unwrapDeclaration(memberAstRaw)
|
|
1390
|
+
if (!S.AST.isObjects(memberAst)) continue
|
|
1391
|
+
|
|
1392
|
+
for (const prop of memberAst.propertySignatures) {
|
|
1393
|
+
const key = prop.name.toString()
|
|
1394
|
+
const fieldDefault = getDefaultFromAst(prop.type)
|
|
1395
|
+
const existingDefault = mergedFields[key] ? getDefaultFromAst(mergedFields[key]!.ast) : undefined
|
|
1396
|
+
|
|
1397
|
+
if (!mergedFields[key] || (fieldDefault !== undefined && existingDefault === undefined)) {
|
|
1398
|
+
mergedFields[key] = { ast: prop.type }
|
|
1399
|
+
}
|
|
1131
1400
|
}
|
|
1132
|
-
|
|
1133
|
-
}, {} as Record<string, any>)
|
|
1401
|
+
}
|
|
1134
1402
|
|
|
1135
|
-
if (Object.keys(
|
|
1403
|
+
if (Object.keys(mergedFields).length === 0) {
|
|
1136
1404
|
return Object.keys(record).length > 0 ? record : undefined
|
|
1137
1405
|
}
|
|
1138
1406
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
acc[key] = defaultsValueFromSchema(value, record[key] || {})
|
|
1407
|
+
return Object.entries(mergedFields).reduce((acc, [key, { ast: propAst }]) => {
|
|
1408
|
+
acc[key] = defaultsValueFromSchema(S.make(propAst), record[key] || {})
|
|
1142
1409
|
return acc
|
|
1143
1410
|
}, record)
|
|
1144
1411
|
}
|
|
1145
1412
|
|
|
1146
1413
|
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
1414
|
if (S.AST.isString(ast)) {
|
|
1177
1415
|
return ""
|
|
1178
1416
|
}
|