@effect-app/vue-components 4.0.0-beta.12 → 4.0.0-beta.120
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/OmegaAutoGen.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +30 -15
- package/dist/types/components/OmegaForm/useOmegaForm.d.ts +3 -5
- package/dist/types/utils/index.d.ts +6 -7
- package/dist/vue-components.es.js +21 -44
- package/dist/vue-components10.es.js +5 -0
- package/dist/vue-components11.es.js +13 -0
- package/dist/vue-components12.es.js +445 -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 +7 -0
- package/dist/vue-components46.es.js +267 -0
- package/dist/vue-components48.es.js +6 -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 +66 -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 +7 -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 +30 -25
- package/src/components/CommandButton.vue +1 -1
- package/src/components/OmegaForm/OmegaAutoGen.vue +25 -30
- package/src/components/OmegaForm/OmegaErrorsInternal.vue +2 -3
- package/src/components/OmegaForm/OmegaFormStuff.ts +513 -353
- package/src/components/OmegaForm/OmegaInternalInput.vue +22 -6
- package/src/components/OmegaForm/useOmegaForm.ts +57 -36
- package/src/reset.css +52 -0
- package/src/utils/index.ts +9 -10
- package/dist/vue-components.es10.js +0 -237
- package/dist/vue-components.es100.js +0 -4
- package/dist/vue-components.es11.js +0 -32
- package/dist/vue-components.es12.js +0 -439
- 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 -725
- package/dist/vue-components.es18.js +0 -143
- package/dist/vue-components.es19.js +0 -6
- package/dist/vue-components.es2.js +0 -30
- package/dist/vue-components.es20.js +0 -13
- package/dist/vue-components.es21.js +0 -5
- package/dist/vue-components.es22.js +0 -26
- package/dist/vue-components.es23.js +0 -6
- package/dist/vue-components.es24.js +0 -10
- package/dist/vue-components.es25.js +0 -57
- package/dist/vue-components.es26.js +0 -71
- package/dist/vue-components.es27.js +0 -8
- package/dist/vue-components.es28.js +0 -8
- package/dist/vue-components.es29.js +0 -5
- package/dist/vue-components.es3.js +0 -16
- package/dist/vue-components.es30.js +0 -5
- package/dist/vue-components.es31.js +0 -4
- package/dist/vue-components.es32.js +0 -4
- package/dist/vue-components.es33.js +0 -4
- package/dist/vue-components.es34.js +0 -4
- package/dist/vue-components.es35.js +0 -19
- package/dist/vue-components.es36.js +0 -320
- package/dist/vue-components.es37.js +0 -563
- package/dist/vue-components.es38.js +0 -29
- package/dist/vue-components.es39.js +0 -54
- package/dist/vue-components.es4.js +0 -52
- package/dist/vue-components.es40.js +0 -66
- package/dist/vue-components.es41.js +0 -6
- package/dist/vue-components.es42.js +0 -6
- package/dist/vue-components.es43.js +0 -26
- package/dist/vue-components.es44.js +0 -77
- package/dist/vue-components.es45.js +0 -42
- package/dist/vue-components.es46.js +0 -316
- package/dist/vue-components.es47.js +0 -101
- package/dist/vue-components.es48.js +0 -33
- package/dist/vue-components.es49.js +0 -4
- package/dist/vue-components.es5.js +0 -52
- package/dist/vue-components.es50.js +0 -4
- package/dist/vue-components.es51.js +0 -4
- package/dist/vue-components.es52.js +0 -113
- package/dist/vue-components.es54.js +0 -9
- package/dist/vue-components.es55.js +0 -34
- package/dist/vue-components.es57.js +0 -194
- package/dist/vue-components.es59.js +0 -40
- package/dist/vue-components.es6.js +0 -69
- package/dist/vue-components.es60.js +0 -85
- package/dist/vue-components.es61.js +0 -43
- package/dist/vue-components.es62.js +0 -7
- package/dist/vue-components.es63.js +0 -6
- package/dist/vue-components.es64.js +0 -25
- package/dist/vue-components.es65.js +0 -7
- package/dist/vue-components.es66.js +0 -23
- package/dist/vue-components.es67.js +0 -32
- package/dist/vue-components.es68.js +0 -24
- package/dist/vue-components.es69.js +0 -14
- package/dist/vue-components.es7.js +0 -83
- package/dist/vue-components.es70.js +0 -7
- package/dist/vue-components.es71.js +0 -21
- package/dist/vue-components.es72.js +0 -11
- package/dist/vue-components.es73.js +0 -33
- package/dist/vue-components.es74.js +0 -50
- package/dist/vue-components.es75.js +0 -28
- package/dist/vue-components.es76.js +0 -103
- package/dist/vue-components.es77.js +0 -84
- package/dist/vue-components.es78.js +0 -17
- package/dist/vue-components.es79.js +0 -34
- package/dist/vue-components.es8.js +0 -63
- package/dist/vue-components.es80.js +0 -23
- package/dist/vue-components.es81.js +0 -14
- package/dist/vue-components.es82.js +0 -115
- package/dist/vue-components.es83.js +0 -5
- package/dist/vue-components.es84.js +0 -4
- package/dist/vue-components.es85.js +0 -4
- package/dist/vue-components.es86.js +0 -18
- package/dist/vue-components.es87.js +0 -72
- package/dist/vue-components.es88.js +0 -10
- package/dist/vue-components.es89.js +0 -4
- package/dist/vue-components.es9.js +0 -21
- package/dist/vue-components.es90.js +0 -17
- package/dist/vue-components.es91.js +0 -13
- package/dist/vue-components.es92.js +0 -67
- package/dist/vue-components.es93.js +0 -58
- package/dist/vue-components.es94.js +0 -19
- package/dist/vue-components.es95.js +0 -35
- package/dist/vue-components.es96.js +0 -31
- package/dist/vue-components.es97.js +0 -44
- package/dist/vue-components.es98.js +0 -4
- package/dist/vue-components.es99.js +0 -46
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import * as AST from "effect/SchemaAST"
|
|
1
|
+
import { Effect, Option, type Record, S } from "effect-app"
|
|
3
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
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"
|
|
5
4
|
import { isObject } from "@vueuse/core"
|
|
6
|
-
import
|
|
5
|
+
import type { Fiber as EffectFiber } from "effect/Fiber"
|
|
6
|
+
import type { Redacted } from "effect/Redacted"
|
|
7
7
|
import { getTransformationFrom, useIntl } from "../../utils"
|
|
8
8
|
import { type OmegaFieldInternalApi } from "./InputProps"
|
|
9
9
|
import { type OF, type OmegaFormReturn } from "./useOmegaForm"
|
|
10
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
|
+
|
|
11
25
|
export type FieldPath<T> = unknown extends T ? string
|
|
12
26
|
// technically we cannot have primitive at the root
|
|
13
|
-
: T extends string | boolean | number | null | undefined | symbol | bigint ? ""
|
|
27
|
+
: T extends string | boolean | number | null | undefined | symbol | bigint | Redacted<any> ? ""
|
|
14
28
|
// technically we cannot have array at the root
|
|
15
29
|
: T extends ReadonlyArray<infer U> ? FieldPath_<U, `[${number}]`>
|
|
16
30
|
: {
|
|
@@ -18,7 +32,7 @@ export type FieldPath<T> = unknown extends T ? string
|
|
|
18
32
|
}[keyof T]
|
|
19
33
|
|
|
20
34
|
export type FieldPath_<T, Path extends string> = unknown extends T ? string
|
|
21
|
-
: T extends string | boolean | number | null | undefined | symbol | bigint ? Path
|
|
35
|
+
: T extends string | boolean | number | null | undefined | symbol | bigint | Redacted<any> ? Path
|
|
22
36
|
: T extends ReadonlyArray<infer U> ? FieldPath_<U, `${Path}[${number}]`> | Path
|
|
23
37
|
: {
|
|
24
38
|
[K in keyof T]: FieldPath_<T[K], `${Path}.${K & string}`>
|
|
@@ -145,7 +159,7 @@ export type FormProps<From, To> =
|
|
|
145
159
|
formApi: OmegaFormParams<From, To>
|
|
146
160
|
meta: any
|
|
147
161
|
value: To
|
|
148
|
-
}) => Promise<any> | Effect.Effect<unknown, any, never>
|
|
162
|
+
}) => Promise<any> | EffectFiber<any, any> | Effect.Effect<unknown, any, never>
|
|
149
163
|
}
|
|
150
164
|
|
|
151
165
|
export type OmegaFormParams<From, To> = FormApi<
|
|
@@ -223,7 +237,13 @@ export type PrefixFromDepth<
|
|
|
223
237
|
_TDepth extends any[]
|
|
224
238
|
> = K
|
|
225
239
|
|
|
226
|
-
|
|
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>>
|
|
227
247
|
|
|
228
248
|
export type FieldValidators<T> = {
|
|
229
249
|
onChangeAsync?: FieldAsyncValidateOrFn<T, any, any>
|
|
@@ -236,6 +256,14 @@ export type FieldValidators<T> = {
|
|
|
236
256
|
export type BaseFieldMeta = {
|
|
237
257
|
required: boolean
|
|
238
258
|
nullableOrUndefined?: false | "undefined" | "null"
|
|
259
|
+
/**
|
|
260
|
+
* True when the schema property is `S.optionalKey` (AST
|
|
261
|
+
* `context.isOptional`) — i.e. the key should be ABSENT from the submitted
|
|
262
|
+
* object when empty, not present with `undefined`. Distinct from
|
|
263
|
+
* `required: false`, which may also mean "empty string is valid" for
|
|
264
|
+
* unconstrained `S.String` fields.
|
|
265
|
+
*/
|
|
266
|
+
isOptionalKey?: boolean
|
|
239
267
|
}
|
|
240
268
|
|
|
241
269
|
export type StringFieldMeta = BaseFieldMeta & {
|
|
@@ -262,13 +290,17 @@ export type SelectFieldMeta = BaseFieldMeta & {
|
|
|
262
290
|
export type MultipleFieldMeta = BaseFieldMeta & {
|
|
263
291
|
type: "multiple"
|
|
264
292
|
members: any[] // TODO: should be non empty array?
|
|
265
|
-
rest: readonly AST.AST[]
|
|
293
|
+
rest: readonly S.AST.AST[]
|
|
266
294
|
}
|
|
267
295
|
|
|
268
296
|
export type BooleanFieldMeta = BaseFieldMeta & {
|
|
269
297
|
type: "boolean"
|
|
270
298
|
}
|
|
271
299
|
|
|
300
|
+
export type DateFieldMeta = BaseFieldMeta & {
|
|
301
|
+
type: "date"
|
|
302
|
+
}
|
|
303
|
+
|
|
272
304
|
export type UnknownFieldMeta = BaseFieldMeta & {
|
|
273
305
|
type: "unknown"
|
|
274
306
|
}
|
|
@@ -279,6 +311,7 @@ export type FieldMeta =
|
|
|
279
311
|
| SelectFieldMeta
|
|
280
312
|
| MultipleFieldMeta
|
|
281
313
|
| BooleanFieldMeta
|
|
314
|
+
| DateFieldMeta
|
|
282
315
|
| UnknownFieldMeta
|
|
283
316
|
|
|
284
317
|
export type MetaRecord<T = string> = {
|
|
@@ -301,34 +334,57 @@ export type CreateMeta =
|
|
|
301
334
|
}
|
|
302
335
|
& (
|
|
303
336
|
| {
|
|
304
|
-
propertySignatures: readonly AST.PropertySignature[]
|
|
337
|
+
propertySignatures: readonly S.AST.PropertySignature[]
|
|
305
338
|
property?: never
|
|
306
339
|
}
|
|
307
340
|
| {
|
|
308
341
|
propertySignatures?: never
|
|
309
|
-
property: AST.AST
|
|
342
|
+
property: S.AST.AST
|
|
310
343
|
}
|
|
311
344
|
)
|
|
312
345
|
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
346
|
+
const unwrapDeclaration = (property: S.AST.AST): S.AST.AST => {
|
|
347
|
+
let current = getTransformationFrom(property)
|
|
348
|
+
|
|
349
|
+
while (S.AST.isDeclaration(current) && current.typeParameters.length > 0) {
|
|
350
|
+
current = getTransformationFrom(current.typeParameters[0]!)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return current
|
|
316
354
|
}
|
|
317
355
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
356
|
+
const isNullishType = (property: S.AST.AST) => S.AST.isUndefined(property) || S.AST.isNull(property)
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Unwrap a single-element Union to its inner type if it's a Literal.
|
|
360
|
+
* After AST.toType, S.Struct({ _tag: S.Literal("X") }) produces Union([Literal("X")])
|
|
361
|
+
* instead of bare Literal("X") like S.TaggedStruct does.
|
|
362
|
+
* TODO: remove after manual _tag deprecation
|
|
363
|
+
*/
|
|
364
|
+
const unwrapSingleLiteralUnion = (ast: S.AST.AST): S.AST.AST =>
|
|
365
|
+
S.AST.isUnion(ast) && ast.types.length === 1 && S.AST.isLiteral(ast.types[0]!)
|
|
366
|
+
? ast.types[0]!
|
|
367
|
+
: ast
|
|
368
|
+
|
|
369
|
+
const getNullableOrUndefined = (property: S.AST.AST) =>
|
|
370
|
+
S.AST.isUnion(property)
|
|
371
|
+
? property.types.find((_) => isNullishType(_))
|
|
372
|
+
: false
|
|
373
|
+
|
|
374
|
+
export const isNullableOrUndefined = (property: false | S.AST.AST | undefined) => {
|
|
375
|
+
if (!property || !S.AST.isUnion(property)) return false
|
|
376
|
+
if (property.types.find((_) => S.AST.isUndefined(_))) {
|
|
321
377
|
return "undefined"
|
|
322
378
|
}
|
|
323
|
-
if (property.types.find((_) =>
|
|
379
|
+
if (property.types.find((_) => S.AST.isNull(_))) return "null"
|
|
324
380
|
return false
|
|
325
381
|
}
|
|
326
382
|
|
|
327
383
|
// 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[] = []
|
|
384
|
+
const unwrapNestedUnions = (types: readonly S.AST.AST[]): readonly S.AST.AST[] => {
|
|
385
|
+
const result: S.AST.AST[] = []
|
|
330
386
|
for (const type of types) {
|
|
331
|
-
if (AST.isUnion(type)) {
|
|
387
|
+
if (S.AST.isUnion(type)) {
|
|
332
388
|
// Recursively unwrap nested unions
|
|
333
389
|
const unwrapped = unwrapNestedUnions(type.types)
|
|
334
390
|
result.push(...unwrapped)
|
|
@@ -339,31 +395,127 @@ const unwrapNestedUnions = (types: readonly AST.AST[]): readonly AST.AST[] => {
|
|
|
339
395
|
return result
|
|
340
396
|
}
|
|
341
397
|
|
|
398
|
+
const getNonNullTypes = (types: readonly S.AST.AST[]) =>
|
|
399
|
+
unwrapNestedUnions(types)
|
|
400
|
+
.map(unwrapDeclaration)
|
|
401
|
+
.filter((_) => !isNullishType(_))
|
|
402
|
+
|
|
403
|
+
const getJsonSchemaAnnotation = (property: S.AST.AST): Record<string, unknown> => {
|
|
404
|
+
const jsonSchema = S.AST.resolve(property)?.jsonSchema
|
|
405
|
+
return jsonSchema && typeof jsonSchema === "object" ? jsonSchema as Record<string, unknown> : {}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const extractDefaultFromLink = (link: any): unknown | undefined => {
|
|
409
|
+
if (!link?.transformation?.decode?.run) return undefined
|
|
410
|
+
try {
|
|
411
|
+
const result = Effect.runSync(link.transformation.decode.run(Option.none())) as Option.Option<unknown>
|
|
412
|
+
return Option.isSome(result) ? result.value : undefined
|
|
413
|
+
} catch {
|
|
414
|
+
return undefined
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const getDefaultFromAst = (property: S.AST.AST) => {
|
|
419
|
+
// 1. Check withConstructorDefault (stored in context.defaultValue)
|
|
420
|
+
const constructorLink = property.context?.defaultValue?.[0]
|
|
421
|
+
const constructorDefault = extractDefaultFromLink(constructorLink)
|
|
422
|
+
if (constructorDefault !== undefined) return constructorDefault
|
|
423
|
+
|
|
424
|
+
// 2. Check withDecodingDefault (stored in encoding)
|
|
425
|
+
const encodingLink = property.encoding?.[0]
|
|
426
|
+
if (encodingLink && property.context?.isOptional) {
|
|
427
|
+
return extractDefaultFromLink(encodingLink)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return undefined
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const getCheckMetas = (property: S.AST.AST): Array<Record<string, any>> => {
|
|
434
|
+
const checks = property.checks ?? []
|
|
435
|
+
|
|
436
|
+
return checks.flatMap((check) => {
|
|
437
|
+
if (check._tag === "FilterGroup") {
|
|
438
|
+
return check.checks.flatMap((inner) => {
|
|
439
|
+
const meta = inner.annotations?.meta
|
|
440
|
+
return meta && typeof meta === "object" ? [meta as Record<string, any>] : []
|
|
441
|
+
})
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const meta = check.annotations?.meta
|
|
445
|
+
return meta && typeof meta === "object" ? [meta as Record<string, any>] : []
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const getFieldMetadataFromAst = (property: S.AST.AST) => {
|
|
450
|
+
const base: Partial<FieldMeta> & Record<string, unknown> = {
|
|
451
|
+
description: S.AST.resolveDescription(property)
|
|
452
|
+
}
|
|
453
|
+
const checks = getCheckMetas(property)
|
|
454
|
+
|
|
455
|
+
if (S.AST.isString(property)) {
|
|
456
|
+
base.type = "string"
|
|
457
|
+
for (const check of checks) {
|
|
458
|
+
switch (check._tag) {
|
|
459
|
+
case "isMinLength":
|
|
460
|
+
base.minLength = check.minLength
|
|
461
|
+
break
|
|
462
|
+
case "isMaxLength":
|
|
463
|
+
base.maxLength = check.maxLength
|
|
464
|
+
break
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (S.AST.resolveTitle(property) === "Email") {
|
|
469
|
+
base.format = "email"
|
|
470
|
+
}
|
|
471
|
+
} else if (S.AST.isNumber(property)) {
|
|
472
|
+
base.type = "number"
|
|
473
|
+
for (const check of checks) {
|
|
474
|
+
switch (check._tag) {
|
|
475
|
+
case "isInt":
|
|
476
|
+
base.refinement = "int"
|
|
477
|
+
break
|
|
478
|
+
case "isGreaterThanOrEqualTo":
|
|
479
|
+
base.minimum = check.minimum
|
|
480
|
+
break
|
|
481
|
+
case "isLessThanOrEqualTo":
|
|
482
|
+
base.maximum = check.maximum
|
|
483
|
+
break
|
|
484
|
+
case "isBetween":
|
|
485
|
+
base.minimum = check.minimum
|
|
486
|
+
base.maximum = check.maximum
|
|
487
|
+
break
|
|
488
|
+
case "isGreaterThan":
|
|
489
|
+
base.exclusiveMinimum = check.exclusiveMinimum
|
|
490
|
+
break
|
|
491
|
+
case "isLessThan":
|
|
492
|
+
base.exclusiveMaximum = check.exclusiveMaximum
|
|
493
|
+
break
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
} else if (S.AST.isBoolean(property)) {
|
|
497
|
+
base.type = "boolean"
|
|
498
|
+
} else if (
|
|
499
|
+
S.AST.isDeclaration(property)
|
|
500
|
+
&& (property.annotations as any)?.typeConstructor?._tag === "Date"
|
|
501
|
+
) {
|
|
502
|
+
base.type = "date"
|
|
503
|
+
} else {
|
|
504
|
+
base.type = "unknown"
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return base
|
|
508
|
+
}
|
|
509
|
+
|
|
342
510
|
export const createMeta = <T = any>(
|
|
343
511
|
{ meta = {}, parent = "", property, propertySignatures }: CreateMeta,
|
|
344
512
|
acc: Partial<MetaRecord<T>> = {}
|
|
345
513
|
): MetaRecord<T> | FieldMeta => {
|
|
346
|
-
|
|
347
|
-
|
|
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
|
|
514
|
+
if (property) {
|
|
515
|
+
property = unwrapDeclaration(property)
|
|
364
516
|
}
|
|
365
517
|
|
|
366
|
-
if (property && AST.isObjects(property)) {
|
|
518
|
+
if (property && S.AST.isObjects(property)) {
|
|
367
519
|
return createMeta<T>({
|
|
368
520
|
meta,
|
|
369
521
|
propertySignatures: property.propertySignatures
|
|
@@ -375,12 +527,11 @@ export const createMeta = <T = any>(
|
|
|
375
527
|
const key = parent ? `${parent}.${p.name.toString()}` : p.name.toString()
|
|
376
528
|
const nullableOrUndefined = isNullableOrUndefined(p.type)
|
|
377
529
|
|
|
378
|
-
|
|
379
|
-
const propertyTitle = p.type.annotations?.title ?? ""
|
|
380
|
-
const isIntField = propertyTitle === "Int" || propertyTitle === "int"
|
|
530
|
+
const isOptionalKey = (p.type as any).context?.isOptional === true
|
|
381
531
|
|
|
382
532
|
// Determine if this field should be required:
|
|
383
533
|
// - For nullable discriminated unions, only _tag should be non-required
|
|
534
|
+
// - optionalKey fields are not required
|
|
384
535
|
// - All other fields should calculate their required status normally
|
|
385
536
|
let isRequired: boolean
|
|
386
537
|
if (meta._isNullableDiscriminatedUnion && p.name.toString() === "_tag") {
|
|
@@ -389,25 +540,18 @@ export const createMeta = <T = any>(
|
|
|
389
540
|
} else if (meta.required === false) {
|
|
390
541
|
// Explicitly set to non-required (legacy behavior for backwards compatibility)
|
|
391
542
|
isRequired = false
|
|
543
|
+
} else if (isOptionalKey) {
|
|
544
|
+
isRequired = false
|
|
392
545
|
} else {
|
|
393
546
|
// Calculate from the property itself
|
|
394
547
|
isRequired = !nullableOrUndefined
|
|
395
548
|
}
|
|
396
549
|
|
|
397
|
-
const typeToProcess = p.type
|
|
398
|
-
if (AST.isUnion(p.type)) {
|
|
399
|
-
|
|
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)
|
|
550
|
+
const typeToProcess = unwrapDeclaration(p.type)
|
|
551
|
+
if (S.AST.isUnion(p.type)) {
|
|
552
|
+
const nonNullTypes = getNonNullTypes(p.type.types)
|
|
407
553
|
|
|
408
|
-
const hasStructMembers = nonNullTypes.some(
|
|
409
|
-
(t) => AST.isObjects(t)
|
|
410
|
-
)
|
|
554
|
+
const hasStructMembers = nonNullTypes.some(S.AST.isObjects)
|
|
411
555
|
|
|
412
556
|
if (hasStructMembers) {
|
|
413
557
|
// Only create parent meta for non-NullOr unions to avoid duplicates
|
|
@@ -422,25 +566,38 @@ export const createMeta = <T = any>(
|
|
|
422
566
|
|
|
423
567
|
// Process each non-null type and merge their metadata
|
|
424
568
|
for (const nonNullType of nonNullTypes) {
|
|
425
|
-
if (AST.isObjects(nonNullType)) {
|
|
569
|
+
if (S.AST.isObjects(nonNullType)) {
|
|
426
570
|
// For discriminated unions (multiple branches):
|
|
427
571
|
// - If the parent union is nullable, only _tag should be non-required
|
|
428
572
|
// - All other fields maintain their normal required status based on their own types
|
|
429
573
|
const isNullableDiscriminatedUnion = nullableOrUndefined && nonNullTypes.length > 1
|
|
430
574
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
)
|
|
575
|
+
const branchMeta = createMeta<T>({
|
|
576
|
+
parent: key,
|
|
577
|
+
propertySignatures: nonNullType.propertySignatures,
|
|
578
|
+
meta: isNullableDiscriminatedUnion ? { _isNullableDiscriminatedUnion: true } : {}
|
|
579
|
+
})
|
|
580
|
+
|
|
581
|
+
// Merge branch metadata, combining select members for shared discriminator fields
|
|
582
|
+
for (const [metaKey, metaValue] of Object.entries(branchMeta)) {
|
|
583
|
+
const existing = acc[metaKey as NestedKeyOf<T>] as FieldMeta | undefined
|
|
584
|
+
if (
|
|
585
|
+
existing && existing.type === "select" && (metaValue as any)?.type === "select"
|
|
586
|
+
) {
|
|
587
|
+
existing.members = [
|
|
588
|
+
...existing.members,
|
|
589
|
+
...(metaValue as SelectFieldMeta).members.filter(
|
|
590
|
+
(m: any) => !existing.members.includes(m)
|
|
591
|
+
)
|
|
592
|
+
]
|
|
593
|
+
} else {
|
|
594
|
+
acc[metaKey as NestedKeyOf<T>] = metaValue as FieldMeta
|
|
595
|
+
}
|
|
596
|
+
}
|
|
439
597
|
}
|
|
440
598
|
}
|
|
441
599
|
} else {
|
|
442
|
-
|
|
443
|
-
const arrayTypes = nonNullTypes.filter(AST.isArrays)
|
|
600
|
+
const arrayTypes = nonNullTypes.filter(S.AST.isArrays)
|
|
444
601
|
if (arrayTypes.length > 0) {
|
|
445
602
|
const arrayType = arrayTypes[0] // Take the first array type
|
|
446
603
|
|
|
@@ -454,8 +611,8 @@ export const createMeta = <T = any>(
|
|
|
454
611
|
|
|
455
612
|
// If the array has struct elements, also create metadata for their properties
|
|
456
613
|
if (arrayType.rest && arrayType.rest.length > 0) {
|
|
457
|
-
const restElement = arrayType.rest[0]
|
|
458
|
-
if (AST.isObjects(restElement)) {
|
|
614
|
+
const restElement = unwrapDeclaration(arrayType.rest[0]!)
|
|
615
|
+
if (S.AST.isObjects(restElement)) {
|
|
459
616
|
for (const prop of restElement.propertySignatures) {
|
|
460
617
|
const propKey = `${key}.${prop.name.toString()}`
|
|
461
618
|
|
|
@@ -473,12 +630,12 @@ export const createMeta = <T = any>(
|
|
|
473
630
|
acc[propKey as NestedKeyOf<T>] = propMeta as FieldMeta
|
|
474
631
|
|
|
475
632
|
if (
|
|
476
|
-
propMeta.type === "multiple" && AST.isArrays(prop.type) && prop
|
|
633
|
+
propMeta.type === "multiple" && S.AST.isArrays(prop.type) && prop
|
|
477
634
|
.type
|
|
478
635
|
.rest && prop.type.rest.length > 0
|
|
479
636
|
) {
|
|
480
|
-
const nestedRestElement = prop.type.rest[0]
|
|
481
|
-
if (AST.isObjects(nestedRestElement)) {
|
|
637
|
+
const nestedRestElement = unwrapDeclaration(prop.type.rest[0]!)
|
|
638
|
+
if (S.AST.isObjects(nestedRestElement)) {
|
|
482
639
|
for (const nestedProp of nestedRestElement.propertySignatures) {
|
|
483
640
|
const nestedPropKey = `${propKey}.${nestedProp.name.toString()}`
|
|
484
641
|
|
|
@@ -513,34 +670,32 @@ export const createMeta = <T = any>(
|
|
|
513
670
|
}
|
|
514
671
|
}
|
|
515
672
|
} else {
|
|
516
|
-
|
|
517
|
-
const unwrappedTypeToProcess = getTransformationFrom(typeToProcess)
|
|
518
|
-
if (AST.isObjects(unwrappedTypeToProcess)) {
|
|
673
|
+
if (S.AST.isObjects(typeToProcess)) {
|
|
519
674
|
Object.assign(
|
|
520
675
|
acc,
|
|
521
676
|
createMeta<T>({
|
|
522
677
|
parent: key,
|
|
523
|
-
propertySignatures:
|
|
678
|
+
propertySignatures: typeToProcess.propertySignatures,
|
|
524
679
|
meta: { required: isRequired, nullableOrUndefined }
|
|
525
680
|
})
|
|
526
681
|
)
|
|
527
|
-
} else if (AST.isArrays(p.type)) {
|
|
682
|
+
} else if (S.AST.isArrays(p.type)) {
|
|
528
683
|
// Check if it has struct elements
|
|
529
684
|
const hasStructElements = p.type.rest.length > 0
|
|
530
|
-
&& AST.isObjects(p.type.rest[0])
|
|
685
|
+
&& S.AST.isObjects(unwrapDeclaration(p.type.rest[0]!))
|
|
531
686
|
|
|
532
687
|
if (hasStructElements) {
|
|
533
688
|
// 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)) {
|
|
689
|
+
const elementType = unwrapDeclaration(p.type.rest[0]!)
|
|
690
|
+
if (S.AST.isObjects(elementType)) {
|
|
536
691
|
// Process each property in the array element
|
|
537
692
|
for (const prop of elementType.propertySignatures) {
|
|
538
693
|
const propKey = `${key}.${prop.name.toString()}`
|
|
539
694
|
|
|
540
695
|
// 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)) {
|
|
696
|
+
if (S.AST.isArrays(prop.type) && prop.type.rest.length > 0) {
|
|
697
|
+
const nestedElementType = unwrapDeclaration(prop.type.rest[0]!)
|
|
698
|
+
if (S.AST.isObjects(nestedElementType)) {
|
|
544
699
|
// Array with struct elements - process nested fields
|
|
545
700
|
for (const nestedProp of nestedElementType.propertySignatures) {
|
|
546
701
|
const nestedKey = `${propKey}.${nestedProp.name.toString()}`
|
|
@@ -592,9 +747,12 @@ export const createMeta = <T = any>(
|
|
|
592
747
|
parent: key,
|
|
593
748
|
property: p.type,
|
|
594
749
|
meta: {
|
|
595
|
-
|
|
750
|
+
// an empty string is valid for a S.String field, so we should not mark it as required
|
|
751
|
+
// TODO: handle this better via the createMeta minLength parsing
|
|
752
|
+
required: isRequired
|
|
753
|
+
&& (!S.AST.isString(typeToProcess) || !!getFieldMetadataFromAst(typeToProcess).minLength),
|
|
596
754
|
nullableOrUndefined,
|
|
597
|
-
...(
|
|
755
|
+
...(isOptionalKey ? { isOptionalKey: true } : {})
|
|
598
756
|
}
|
|
599
757
|
})
|
|
600
758
|
|
|
@@ -607,19 +765,30 @@ export const createMeta = <T = any>(
|
|
|
607
765
|
|
|
608
766
|
if (property) {
|
|
609
767
|
const nullableOrUndefined = getNullableOrUndefined(property)
|
|
768
|
+
property = unwrapDeclaration(property)
|
|
610
769
|
|
|
611
770
|
if (!Object.hasOwnProperty.call(meta, "required")) {
|
|
612
771
|
meta["required"] = !nullableOrUndefined
|
|
613
772
|
}
|
|
614
773
|
|
|
615
|
-
if (AST.isUnion(property)) {
|
|
616
|
-
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
)
|
|
774
|
+
if (S.AST.isUnion(property)) {
|
|
775
|
+
const unwrappedTypes = unwrapNestedUnions(property.types).map(unwrapDeclaration)
|
|
776
|
+
const nonNullTypes = unwrappedTypes.filter((t) => !isNullishType(t))
|
|
777
|
+
|
|
778
|
+
// Unwrap single-element unions when the literal is a boolean
|
|
779
|
+
// (effect-app's S.Literal wraps as S.Literals([x]) → Union([Literal(x)]))
|
|
780
|
+
// Don't unwrap string/number literals — they may be discriminator values in a union
|
|
781
|
+
if (
|
|
782
|
+
nonNullTypes.length === 1
|
|
783
|
+
&& S.AST.isLiteral(nonNullTypes[0]!)
|
|
784
|
+
&& typeof nonNullTypes[0]!.literal === "boolean"
|
|
785
|
+
) {
|
|
786
|
+
return createMeta<T>({ parent, meta, property: nonNullTypes[0]! })
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const nonNullType = nonNullTypes[0]!
|
|
621
790
|
|
|
622
|
-
if (AST.isObjects(nonNullType)) {
|
|
791
|
+
if (S.AST.isObjects(nonNullType)) {
|
|
623
792
|
return createMeta<T>({
|
|
624
793
|
propertySignatures: nonNullType.propertySignatures,
|
|
625
794
|
parent,
|
|
@@ -627,11 +796,13 @@ export const createMeta = <T = any>(
|
|
|
627
796
|
})
|
|
628
797
|
}
|
|
629
798
|
|
|
630
|
-
|
|
799
|
+
// TODO: remove after manual _tag deprecation — unwrap legacy S.Struct({ _tag: S.Literal("X") }) pattern
|
|
800
|
+
const resolvedTypes = unwrappedTypes.map(unwrapSingleLiteralUnion)
|
|
801
|
+
if (resolvedTypes.every((_) => isNullishType(_) || S.AST.isLiteral(_))) {
|
|
631
802
|
return {
|
|
632
803
|
...meta,
|
|
633
804
|
type: "select",
|
|
634
|
-
members:
|
|
805
|
+
members: resolvedTypes.filter(S.AST.isLiteral).map((t) => t.literal)
|
|
635
806
|
} as FieldMeta
|
|
636
807
|
}
|
|
637
808
|
|
|
@@ -645,7 +816,7 @@ export const createMeta = <T = any>(
|
|
|
645
816
|
} as FieldMeta
|
|
646
817
|
}
|
|
647
818
|
|
|
648
|
-
if (AST.isArrays(property)) {
|
|
819
|
+
if (S.AST.isArrays(property)) {
|
|
649
820
|
return {
|
|
650
821
|
...meta,
|
|
651
822
|
type: "multiple",
|
|
@@ -654,38 +825,15 @@ export const createMeta = <T = any>(
|
|
|
654
825
|
} as FieldMeta
|
|
655
826
|
}
|
|
656
827
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
|
828
|
+
if (S.AST.isLiteral(property)) {
|
|
829
|
+
return {
|
|
830
|
+
...meta,
|
|
831
|
+
type: "select",
|
|
832
|
+
members: [property.literal]
|
|
833
|
+
} as FieldMeta
|
|
683
834
|
}
|
|
684
835
|
|
|
685
|
-
|
|
686
|
-
if (!Object.hasOwnProperty.call(meta, "required")) {
|
|
687
|
-
meta["required"] = !nullableOrUndefined
|
|
688
|
-
}
|
|
836
|
+
meta = { ...getJsonSchemaAnnotation(property), ...getFieldMetadataFromAst(property), ...meta }
|
|
689
837
|
|
|
690
838
|
return meta as FieldMeta
|
|
691
839
|
}
|
|
@@ -711,27 +859,21 @@ const flattenMeta = <T>(meta: MetaRecord<T> | FieldMeta, parentKey: string = "")
|
|
|
711
859
|
return result
|
|
712
860
|
}
|
|
713
861
|
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
const metadataFromAst = <_From, To>(
|
|
717
|
-
schema: any // v4 Schema type is complex, use any for now
|
|
862
|
+
const metadataFromAst = <From, To>(
|
|
863
|
+
schema: S.Codec<To, From, never>
|
|
718
864
|
): { meta: MetaRecord<To>; defaultValues: Record<string, any>; unionMeta: Record<string, MetaRecord<To>> } => {
|
|
719
|
-
const ast = schema.ast
|
|
865
|
+
const ast = unwrapDeclaration(schema.ast)
|
|
720
866
|
const newMeta: MetaRecord<To> = {}
|
|
721
867
|
const defaultValues: Record<string, any> = {}
|
|
722
868
|
const unionMeta: Record<string, MetaRecord<To>> = {}
|
|
723
869
|
|
|
724
870
|
// Handle root-level Union types (discriminated unions)
|
|
725
|
-
if (AST.isUnion(ast)) {
|
|
726
|
-
const types = ast.types
|
|
727
|
-
|
|
871
|
+
if (S.AST.isUnion(ast)) {
|
|
728
872
|
// 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)
|
|
873
|
+
const nonNullTypes = getNonNullTypes(ast.types)
|
|
732
874
|
|
|
733
875
|
// Check if this is a discriminated union (all members are structs)
|
|
734
|
-
const allStructs = nonNullTypes.every(
|
|
876
|
+
const allStructs = nonNullTypes.every(S.AST.isObjects)
|
|
735
877
|
|
|
736
878
|
if (allStructs && nonNullTypes.length > 0) {
|
|
737
879
|
// Extract discriminator values from each union member
|
|
@@ -739,16 +881,32 @@ const metadataFromAst = <_From, To>(
|
|
|
739
881
|
|
|
740
882
|
// Store metadata for each union member by its tag value
|
|
741
883
|
for (const memberType of nonNullTypes) {
|
|
742
|
-
if (AST.isObjects(memberType)) {
|
|
884
|
+
if (S.AST.isObjects(memberType)) {
|
|
743
885
|
// Find the discriminator field (usually _tag)
|
|
744
886
|
const tagProp = memberType.propertySignatures.find(
|
|
745
|
-
(p
|
|
887
|
+
(p) => p.name.toString() === "_tag"
|
|
746
888
|
)
|
|
747
889
|
|
|
748
890
|
let tagValue: string | null = null
|
|
749
|
-
|
|
750
|
-
|
|
891
|
+
// TODO: remove after manual _tag deprecation — unwrap legacy S.Struct({ _tag: S.Literal("X") }) pattern
|
|
892
|
+
const resolvedTagType = tagProp ? unwrapSingleLiteralUnion(tagProp.type) : null
|
|
893
|
+
if (resolvedTagType && S.AST.isLiteral(resolvedTagType)) {
|
|
894
|
+
tagValue = resolvedTagType.literal as string
|
|
751
895
|
discriminatorValues.push(tagValue)
|
|
896
|
+
// Warn if the tag was wrapped in a single-element Union (legacy pattern)
|
|
897
|
+
if (
|
|
898
|
+
tagProp
|
|
899
|
+
&& S.AST.isUnion(tagProp.type)
|
|
900
|
+
&& isDevelopmentEnvironment()
|
|
901
|
+
&& tagValue != null
|
|
902
|
+
&& !legacyTagWarningEmittedFor.has(tagValue)
|
|
903
|
+
) {
|
|
904
|
+
legacyTagWarningEmittedFor.add(tagValue)
|
|
905
|
+
console.warn(
|
|
906
|
+
`[OmegaForm] Union member with _tag "${tagValue}" uses S.Struct({ _tag: S.Literal("${tagValue}"), ... }). `
|
|
907
|
+
+ `Please migrate to S.TaggedStruct("${tagValue}", { ... }) for cleaner AST handling.`
|
|
908
|
+
)
|
|
909
|
+
}
|
|
752
910
|
}
|
|
753
911
|
|
|
754
912
|
// Create metadata for this member's properties
|
|
@@ -779,7 +937,7 @@ const metadataFromAst = <_From, To>(
|
|
|
779
937
|
}
|
|
780
938
|
}
|
|
781
939
|
|
|
782
|
-
if (AST.isObjects(ast)) {
|
|
940
|
+
if (S.AST.isObjects(ast)) {
|
|
783
941
|
const meta = createMeta<To>({
|
|
784
942
|
propertySignatures: ast.propertySignatures
|
|
785
943
|
})
|
|
@@ -808,16 +966,80 @@ const metadataFromAst = <_From, To>(
|
|
|
808
966
|
return { meta: newMeta, defaultValues, unionMeta }
|
|
809
967
|
}
|
|
810
968
|
|
|
969
|
+
/*
|
|
970
|
+
* Checks if an AST node is a S.Redacted Declaration without encoding.
|
|
971
|
+
* These need to be swapped to S.RedactedFromValue for form usage
|
|
972
|
+
* because S.Redacted expects Redacted objects, not plain strings.
|
|
973
|
+
*/
|
|
974
|
+
const isRedactedWithoutEncoding = (ast: S.AST.AST): boolean =>
|
|
975
|
+
S.AST.isDeclaration(ast)
|
|
976
|
+
&& (ast.annotations as any)?.typeConstructor?._tag === "effect/Redacted"
|
|
977
|
+
&& !ast.encoding
|
|
978
|
+
|
|
979
|
+
/*
|
|
980
|
+
* Creates a form-compatible schema by replacing S.Redacted(X) with
|
|
981
|
+
* S.RedactedFromValue(X). S.Redacted is a Declaration that expects
|
|
982
|
+
* Redacted<A> on both encoded and type sides, so form inputs (which
|
|
983
|
+
* produce plain strings) fail validation. S.RedactedFromValue accepts
|
|
984
|
+
* plain values on the encoded side and wraps them in Redacted on decode.
|
|
985
|
+
*/
|
|
986
|
+
export const toFormSchema = <From, To>(
|
|
987
|
+
schema: S.Codec<To, From, never>
|
|
988
|
+
): S.Codec<To, From, never> => {
|
|
989
|
+
const ast = schema.ast
|
|
990
|
+
const objAst = S.AST.isObjects(ast)
|
|
991
|
+
? ast
|
|
992
|
+
: S.AST.isDeclaration(ast)
|
|
993
|
+
? S.AST.toEncoded(ast)
|
|
994
|
+
: null
|
|
995
|
+
|
|
996
|
+
if (!objAst || !("propertySignatures" in objAst)) return schema
|
|
997
|
+
|
|
998
|
+
let hasRedacted = false
|
|
999
|
+
const props: Record<string, S.Struct.Fields[string]> = {}
|
|
1000
|
+
|
|
1001
|
+
for (const p of objAst.propertySignatures) {
|
|
1002
|
+
if (isRedactedWithoutEncoding(p.type)) {
|
|
1003
|
+
hasRedacted = true
|
|
1004
|
+
const innerSchema = S.make((p.type as S.AST.Declaration).typeParameters[0]!)
|
|
1005
|
+
props[p.name as string] = S.RedactedFromValue(innerSchema)
|
|
1006
|
+
} else if (S.AST.isUnion(p.type)) {
|
|
1007
|
+
const types = p.type.types
|
|
1008
|
+
const redactedType = types.find(isRedactedWithoutEncoding)
|
|
1009
|
+
if (redactedType) {
|
|
1010
|
+
hasRedacted = true
|
|
1011
|
+
const innerSchema = S.make((redactedType as S.AST.Declaration).typeParameters[0]!)
|
|
1012
|
+
const hasNull = types.some(S.AST.isNull)
|
|
1013
|
+
const hasUndefined = types.some(S.AST.isUndefined)
|
|
1014
|
+
const base = S.RedactedFromValue(innerSchema)
|
|
1015
|
+
props[p.name as string] = hasNull && hasUndefined
|
|
1016
|
+
? S.NullishOr(base)
|
|
1017
|
+
: hasNull
|
|
1018
|
+
? S.NullOr(base)
|
|
1019
|
+
: hasUndefined
|
|
1020
|
+
? S.UndefinedOr(base)
|
|
1021
|
+
: base
|
|
1022
|
+
} else {
|
|
1023
|
+
props[p.name as string] = S.make(p.type)
|
|
1024
|
+
}
|
|
1025
|
+
} else {
|
|
1026
|
+
props[p.name as string] = S.make(p.type)
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
return hasRedacted ? S.Struct(props) as unknown as S.Codec<To, From, never> : schema
|
|
1031
|
+
}
|
|
1032
|
+
|
|
811
1033
|
export const duplicateSchema = <From, To>(
|
|
812
1034
|
schema: S.Codec<To, From, never>
|
|
813
1035
|
) => {
|
|
814
1036
|
return schema
|
|
815
1037
|
}
|
|
816
1038
|
|
|
817
|
-
export const generateMetaFromSchema = <
|
|
818
|
-
schema:
|
|
1039
|
+
export const generateMetaFromSchema = <From, To>(
|
|
1040
|
+
schema: S.Codec<To, From, never>
|
|
819
1041
|
): {
|
|
820
|
-
schema:
|
|
1042
|
+
schema: S.Codec<To, From, never>
|
|
821
1043
|
meta: MetaRecord<To>
|
|
822
1044
|
unionMeta: Record<string, MetaRecord<To>>
|
|
823
1045
|
} => {
|
|
@@ -833,168 +1055,138 @@ export const generateInputStandardSchemaFromFieldMeta = (
|
|
|
833
1055
|
if (!trans) {
|
|
834
1056
|
trans = useIntl().trans
|
|
835
1057
|
}
|
|
836
|
-
let schema:
|
|
837
|
-
|
|
1058
|
+
let schema: any
|
|
838
1059
|
switch (meta.type) {
|
|
839
|
-
case "string":
|
|
840
|
-
schema =
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
(s) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s) || trans("validation.email.invalid"),
|
|
848
|
-
{ title: "email format" }
|
|
849
|
-
)
|
|
850
|
-
)
|
|
851
|
-
}
|
|
1060
|
+
case "string":
|
|
1061
|
+
schema = meta.format === "email"
|
|
1062
|
+
? S.Email.annotate({
|
|
1063
|
+
message: trans("validation.email.invalid")
|
|
1064
|
+
})
|
|
1065
|
+
: S.String.annotate({
|
|
1066
|
+
message: trans("validation.empty")
|
|
1067
|
+
})
|
|
852
1068
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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
|
-
}
|
|
1069
|
+
if (meta.required) {
|
|
1070
|
+
schema = schema.check(S.isMinLength(1, {
|
|
1071
|
+
message: trans("validation.empty")
|
|
1072
|
+
}))
|
|
864
1073
|
}
|
|
865
1074
|
|
|
866
1075
|
if (typeof meta.maxLength === "number") {
|
|
867
|
-
schema = schema.check(
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1076
|
+
schema = schema.check(S.isMaxLength(meta.maxLength, {
|
|
1077
|
+
message: trans("validation.string.maxLength", {
|
|
1078
|
+
maxLength: meta.maxLength
|
|
1079
|
+
})
|
|
1080
|
+
}))
|
|
1081
|
+
}
|
|
1082
|
+
if (typeof meta.minLength === "number") {
|
|
1083
|
+
schema = schema.check(S.isMinLength(meta.minLength, {
|
|
1084
|
+
message: trans("validation.string.minLength", {
|
|
1085
|
+
minLength: meta.minLength
|
|
1086
|
+
})
|
|
1087
|
+
}))
|
|
873
1088
|
}
|
|
874
1089
|
break
|
|
875
|
-
}
|
|
876
1090
|
|
|
877
|
-
case "number":
|
|
1091
|
+
case "number":
|
|
878
1092
|
if (meta.refinement === "int") {
|
|
879
|
-
schema = S
|
|
1093
|
+
schema = S
|
|
1094
|
+
.Number
|
|
1095
|
+
.annotate({
|
|
1096
|
+
message: trans("validation.empty")
|
|
1097
|
+
})
|
|
1098
|
+
.check(S.isInt({
|
|
1099
|
+
message: trans("validation.integer.expected", { actualValue: "NaN" })
|
|
1100
|
+
}))
|
|
880
1101
|
} else {
|
|
881
|
-
schema = S.
|
|
1102
|
+
schema = S.Finite.annotate({
|
|
1103
|
+
message: trans("validation.number.expected", { actualValue: "NaN" })
|
|
1104
|
+
})
|
|
1105
|
+
|
|
1106
|
+
if (meta.required) {
|
|
1107
|
+
schema = schema.annotate({
|
|
1108
|
+
message: trans("validation.empty")
|
|
1109
|
+
})
|
|
1110
|
+
}
|
|
882
1111
|
}
|
|
883
1112
|
|
|
884
|
-
// Apply numeric validations
|
|
885
1113
|
if (typeof meta.minimum === "number") {
|
|
886
|
-
schema = schema.check(
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
),
|
|
893
|
-
{ title: `>=${meta.minimum}` }
|
|
894
|
-
)
|
|
895
|
-
)
|
|
1114
|
+
schema = schema.check(S.isGreaterThanOrEqualTo(meta.minimum, {
|
|
1115
|
+
message: trans(meta.minimum === 0 ? "validation.number.positive" : "validation.number.min", {
|
|
1116
|
+
minimum: meta.minimum,
|
|
1117
|
+
isExclusive: true
|
|
1118
|
+
})
|
|
1119
|
+
}))
|
|
896
1120
|
}
|
|
897
|
-
|
|
898
1121
|
if (typeof meta.maximum === "number") {
|
|
899
|
-
schema = schema.check(
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}),
|
|
906
|
-
{ title: `<=${meta.maximum}` }
|
|
907
|
-
)
|
|
908
|
-
)
|
|
1122
|
+
schema = schema.check(S.isLessThanOrEqualTo(meta.maximum, {
|
|
1123
|
+
message: trans("validation.number.max", {
|
|
1124
|
+
maximum: meta.maximum,
|
|
1125
|
+
isExclusive: true
|
|
1126
|
+
})
|
|
1127
|
+
}))
|
|
909
1128
|
}
|
|
910
|
-
|
|
911
1129
|
if (typeof meta.exclusiveMinimum === "number") {
|
|
912
|
-
schema = schema.check(
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
),
|
|
919
|
-
{ title: `>${meta.exclusiveMinimum}` }
|
|
920
|
-
)
|
|
921
|
-
)
|
|
1130
|
+
schema = schema.check(S.isGreaterThan(meta.exclusiveMinimum, {
|
|
1131
|
+
message: trans(meta.exclusiveMinimum === 0 ? "validation.number.positive" : "validation.number.min", {
|
|
1132
|
+
minimum: meta.exclusiveMinimum,
|
|
1133
|
+
isExclusive: false
|
|
1134
|
+
})
|
|
1135
|
+
}))
|
|
922
1136
|
}
|
|
923
|
-
|
|
924
1137
|
if (typeof meta.exclusiveMaximum === "number") {
|
|
925
|
-
schema = schema.check(
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}),
|
|
932
|
-
{ title: `<${meta.exclusiveMaximum}` }
|
|
933
|
-
)
|
|
934
|
-
)
|
|
1138
|
+
schema = schema.check(S.isLessThan(meta.exclusiveMaximum, {
|
|
1139
|
+
message: trans("validation.number.max", {
|
|
1140
|
+
maximum: meta.exclusiveMaximum,
|
|
1141
|
+
isExclusive: false
|
|
1142
|
+
})
|
|
1143
|
+
}))
|
|
935
1144
|
}
|
|
936
1145
|
break
|
|
937
|
-
|
|
1146
|
+
case "select":
|
|
1147
|
+
schema = S.Literals(meta.members as [any, ...any[]]).annotate({
|
|
1148
|
+
message: trans("validation.not_a_valid", {
|
|
1149
|
+
type: "select",
|
|
1150
|
+
message: meta.members.join(", ")
|
|
1151
|
+
})
|
|
1152
|
+
})
|
|
938
1153
|
|
|
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
1154
|
break
|
|
950
|
-
}
|
|
951
1155
|
|
|
952
|
-
case "multiple":
|
|
953
|
-
schema = S.Array(S.String)
|
|
1156
|
+
case "multiple":
|
|
1157
|
+
schema = S.Array(S.String).annotate({
|
|
1158
|
+
message: trans("validation.not_a_valid", {
|
|
1159
|
+
type: "multiple",
|
|
1160
|
+
message: meta.members.join(", ")
|
|
1161
|
+
})
|
|
1162
|
+
})
|
|
954
1163
|
break
|
|
955
|
-
}
|
|
956
1164
|
|
|
957
|
-
case "boolean":
|
|
1165
|
+
case "boolean":
|
|
958
1166
|
schema = S.Boolean
|
|
959
1167
|
break
|
|
960
|
-
}
|
|
961
1168
|
|
|
962
|
-
case "
|
|
1169
|
+
case "date":
|
|
1170
|
+
schema = S.Date
|
|
1171
|
+
break
|
|
1172
|
+
|
|
1173
|
+
case "unknown":
|
|
963
1174
|
schema = S.Unknown
|
|
964
1175
|
break
|
|
965
|
-
}
|
|
966
1176
|
|
|
967
|
-
default:
|
|
968
|
-
|
|
1177
|
+
default:
|
|
1178
|
+
// For any unhandled types, use Unknown schema to prevent undefined errors
|
|
1179
|
+
console.warn(`Unhandled field type: ${meta}`)
|
|
969
1180
|
schema = S.Unknown
|
|
970
1181
|
break
|
|
971
|
-
}
|
|
972
1182
|
}
|
|
973
|
-
|
|
974
|
-
// Wrap in union with null/undefined if not required
|
|
975
1183
|
if (!meta.required) {
|
|
976
|
-
|
|
977
|
-
schema = S.Union([schema, S.Null, S.Undefined])
|
|
1184
|
+
schema = S.NullishOr(schema)
|
|
978
1185
|
}
|
|
979
|
-
|
|
980
|
-
return
|
|
1186
|
+
const result = S.toStandardSchemaV1(schema as any)
|
|
1187
|
+
return result
|
|
981
1188
|
}
|
|
982
1189
|
|
|
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
1190
|
export type OmegaAutoGenMeta<
|
|
999
1191
|
From extends Record<PropertyKey, any>,
|
|
1000
1192
|
To extends Record<PropertyKey, any>,
|
|
@@ -1037,18 +1229,8 @@ export function deepMerge(target: any, source: any) {
|
|
|
1037
1229
|
return result
|
|
1038
1230
|
}
|
|
1039
1231
|
|
|
1040
|
-
// Type definitions for schemas with fields and members
|
|
1041
|
-
type SchemaWithFields = {
|
|
1042
|
-
fields: Record<string, S.Top>
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
1232
|
type SchemaWithMembers = {
|
|
1046
|
-
members: readonly S.
|
|
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"
|
|
1233
|
+
members: readonly S.Schema<any>[]
|
|
1052
1234
|
}
|
|
1053
1235
|
|
|
1054
1236
|
function hasMembers(schema: any): schema is SchemaWithMembers {
|
|
@@ -1057,10 +1239,15 @@ function hasMembers(schema: any): schema is SchemaWithMembers {
|
|
|
1057
1239
|
|
|
1058
1240
|
// Internal implementation with WeakSet tracking
|
|
1059
1241
|
export const defaultsValueFromSchema = (
|
|
1060
|
-
schema: S.
|
|
1242
|
+
schema: S.Schema<any>,
|
|
1061
1243
|
record: Record<string, any> = {}
|
|
1062
1244
|
): any => {
|
|
1063
1245
|
const ast = schema.ast
|
|
1246
|
+
const defaultValue = getDefaultFromAst(ast)
|
|
1247
|
+
|
|
1248
|
+
if (defaultValue !== undefined) {
|
|
1249
|
+
return defaultValue
|
|
1250
|
+
}
|
|
1064
1251
|
|
|
1065
1252
|
if (isNullableOrUndefined(schema.ast) === "null") {
|
|
1066
1253
|
return null
|
|
@@ -1069,106 +1256,79 @@ export const defaultsValueFromSchema = (
|
|
|
1069
1256
|
return undefined
|
|
1070
1257
|
}
|
|
1071
1258
|
|
|
1072
|
-
// Handle
|
|
1073
|
-
|
|
1074
|
-
|
|
1259
|
+
// Handle structs via AST (covers plain structs, transformed schemas like decodeTo, ExtendedClass, etc.)
|
|
1260
|
+
const objectsAst = S.AST.isObjects(ast)
|
|
1261
|
+
? ast
|
|
1262
|
+
: S.AST.isDeclaration(ast)
|
|
1263
|
+
? unwrapDeclaration(ast)
|
|
1264
|
+
: undefined
|
|
1265
|
+
if (objectsAst && S.AST.isObjects(objectsAst)) {
|
|
1266
|
+
const result: Record<string, any> = {}
|
|
1075
1267
|
|
|
1076
|
-
for (const prop of
|
|
1268
|
+
for (const prop of objectsAst.propertySignatures) {
|
|
1077
1269
|
const key = prop.name.toString()
|
|
1078
1270
|
const propType = prop.type
|
|
1079
1271
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
propSchema = (schema as any).fields[key]
|
|
1085
|
-
} else {
|
|
1086
|
-
propSchema = S.make(propType)
|
|
1272
|
+
const propDefault = getDefaultFromAst(propType)
|
|
1273
|
+
if (propDefault !== undefined) {
|
|
1274
|
+
result[key] = propDefault
|
|
1275
|
+
continue
|
|
1087
1276
|
}
|
|
1088
1277
|
|
|
1089
|
-
|
|
1278
|
+
const propSchema = S.make(propType)
|
|
1090
1279
|
const propValue = defaultsValueFromSchema(propSchema, record[key] || {})
|
|
1091
1280
|
|
|
1092
1281
|
if (propValue !== undefined) {
|
|
1093
1282
|
result[key] = propValue
|
|
1283
|
+
} else if (isNullableOrUndefined(propType) === "undefined") {
|
|
1284
|
+
result[key] = undefined
|
|
1094
1285
|
}
|
|
1095
1286
|
}
|
|
1096
1287
|
|
|
1097
|
-
return result
|
|
1288
|
+
return { ...result, ...record }
|
|
1098
1289
|
}
|
|
1099
1290
|
|
|
1100
|
-
//
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1291
|
+
// Handle unions via AST or schema-level .members
|
|
1292
|
+
const unionTypes = S.AST.isUnion(ast)
|
|
1293
|
+
? ast.types
|
|
1294
|
+
: hasMembers(schema)
|
|
1295
|
+
? schema.members.map((m) => m.ast)
|
|
1296
|
+
: undefined
|
|
1297
|
+
if (unionTypes) {
|
|
1298
|
+
const mergedFields: Record<string, { ast: S.AST.AST }> = {}
|
|
1299
|
+
|
|
1300
|
+
for (const memberAstRaw of unionTypes) {
|
|
1301
|
+
const memberAst = unwrapDeclaration(memberAstRaw)
|
|
1302
|
+
if (!S.AST.isObjects(memberAst)) continue
|
|
1303
|
+
|
|
1304
|
+
for (const prop of memberAst.propertySignatures) {
|
|
1305
|
+
const key = prop.name.toString()
|
|
1306
|
+
const fieldDefault = getDefaultFromAst(prop.type)
|
|
1307
|
+
const existingDefault = mergedFields[key] ? getDefaultFromAst(mergedFields[key]!.ast) : undefined
|
|
1308
|
+
|
|
1309
|
+
if (!mergedFields[key] || (fieldDefault !== undefined && existingDefault === undefined)) {
|
|
1310
|
+
mergedFields[key] = { ast: prop.type }
|
|
1114
1311
|
}
|
|
1115
1312
|
}
|
|
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
1313
|
}
|
|
1123
1314
|
|
|
1124
|
-
|
|
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>)
|
|
1315
|
+
if (Object.keys(mergedFields).length === 0) {
|
|
1316
|
+
return Object.keys(record).length > 0 ? record : undefined
|
|
1317
|
+
}
|
|
1151
1318
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
acc[key] = defaultsValueFromSchema(value, record[key] || {})
|
|
1319
|
+
return Object.entries(mergedFields).reduce((acc, [key, { ast: propAst }]) => {
|
|
1320
|
+
acc[key] = defaultsValueFromSchema(S.make(propAst), record[key] || {})
|
|
1155
1321
|
return acc
|
|
1156
1322
|
}, record)
|
|
1157
1323
|
}
|
|
1158
1324
|
|
|
1159
1325
|
if (Object.keys(record).length === 0) {
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
|
1326
|
+
if (S.AST.isString(ast)) {
|
|
1327
|
+
return ""
|
|
1165
1328
|
}
|
|
1166
|
-
}
|
|
1167
1329
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
if (AST.isBoolean(ast)) {
|
|
1172
|
-
return false
|
|
1330
|
+
if (S.AST.isBoolean(ast)) {
|
|
1331
|
+
return false
|
|
1332
|
+
}
|
|
1173
1333
|
}
|
|
1174
1334
|
}
|