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