@effect-app/vue-components 4.0.0-beta.6 → 4.0.0-beta.60
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 +51 -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 +440 -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 +29 -25
- package/src/components/OmegaForm/OmegaAutoGen.vue +25 -30
- package/src/components/OmegaForm/OmegaErrorsInternal.vue +2 -3
- package/src/components/OmegaForm/OmegaFormStuff.ts +498 -357
- package/src/components/OmegaForm/OmegaInternalInput.vue +9 -5
- package/src/components/OmegaForm/useOmegaForm.ts +57 -36
- package/src/reset.css +51 -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 -194
- package/dist/vue-components.es38.js +0 -320
- package/dist/vue-components.es39.js +0 -563
- package/dist/vue-components.es4.js +0 -52
- package/dist/vue-components.es40.js +0 -29
- package/dist/vue-components.es41.js +0 -54
- package/dist/vue-components.es42.js +0 -66
- package/dist/vue-components.es43.js +0 -6
- package/dist/vue-components.es44.js +0 -6
- package/dist/vue-components.es45.js +0 -26
- package/dist/vue-components.es46.js +0 -77
- package/dist/vue-components.es47.js +0 -42
- package/dist/vue-components.es48.js +0 -316
- package/dist/vue-components.es49.js +0 -101
- package/dist/vue-components.es5.js +0 -52
- package/dist/vue-components.es50.js +0 -33
- package/dist/vue-components.es51.js +0 -4
- package/dist/vue-components.es52.js +0 -4
- package/dist/vue-components.es53.js +0 -4
- package/dist/vue-components.es54.js +0 -113
- package/dist/vue-components.es56.js +0 -9
- package/dist/vue-components.es57.js +0 -34
- 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 -23
- package/dist/vue-components.es79.js +0 -14
- package/dist/vue-components.es8.js +0 -63
- package/dist/vue-components.es80.js +0 -115
- package/dist/vue-components.es81.js +0 -5
- package/dist/vue-components.es82.js +0 -34
- package/dist/vue-components.es83.js +0 -4
- package/dist/vue-components.es84.js +0 -4
- package/dist/vue-components.es85.js +0 -17
- 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>
|
|
@@ -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 withDefaultConstructor (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,7 @@ export const createMeta = <T = any>(
|
|
|
654
814
|
} as FieldMeta
|
|
655
815
|
}
|
|
656
816
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
meta = { ...JSONAnnotation, ...meta }
|
|
660
|
-
|
|
661
|
-
// check the title annotation BEFORE following "from" to detect refinements like S.Int
|
|
662
|
-
let titleType = property.annotations?.title ?? "unknown"
|
|
663
|
-
|
|
664
|
-
// Detect basic types from AST if no title annotation
|
|
665
|
-
if (titleType === "unknown") {
|
|
666
|
-
if (AST.isString(property)) {
|
|
667
|
-
titleType = "string"
|
|
668
|
-
} else if (AST.isNumber(property)) {
|
|
669
|
-
titleType = "number"
|
|
670
|
-
} else if (AST.isBoolean(property)) {
|
|
671
|
-
titleType = "boolean"
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// if this is S.Int (a refinement), set the type and skip following "from"
|
|
676
|
-
// otherwise we'd lose the "Int" information and get "number" instead
|
|
677
|
-
if (titleType === "Int" || titleType === "int") {
|
|
678
|
-
meta["type"] = "number"
|
|
679
|
-
meta["refinement"] = "int"
|
|
680
|
-
// don't follow "from" for Int refinements
|
|
681
|
-
} else {
|
|
682
|
-
meta["type"] = titleType
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Always ensure required is set before returning
|
|
686
|
-
if (!Object.hasOwnProperty.call(meta, "required")) {
|
|
687
|
-
meta["required"] = !nullableOrUndefined
|
|
688
|
-
}
|
|
817
|
+
meta = { ...getJsonSchemaAnnotation(property), ...getFieldMetadataFromAst(property), ...meta }
|
|
689
818
|
|
|
690
819
|
return meta as FieldMeta
|
|
691
820
|
}
|
|
@@ -711,27 +840,21 @@ const flattenMeta = <T>(meta: MetaRecord<T> | FieldMeta, parentKey: string = "")
|
|
|
711
840
|
return result
|
|
712
841
|
}
|
|
713
842
|
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
const metadataFromAst = <_From, To>(
|
|
717
|
-
schema: any // v4 Schema type is complex, use any for now
|
|
843
|
+
const metadataFromAst = <From, To>(
|
|
844
|
+
schema: S.Codec<To, From, never>
|
|
718
845
|
): { meta: MetaRecord<To>; defaultValues: Record<string, any>; unionMeta: Record<string, MetaRecord<To>> } => {
|
|
719
|
-
const ast = schema.ast
|
|
846
|
+
const ast = unwrapDeclaration(schema.ast)
|
|
720
847
|
const newMeta: MetaRecord<To> = {}
|
|
721
848
|
const defaultValues: Record<string, any> = {}
|
|
722
849
|
const unionMeta: Record<string, MetaRecord<To>> = {}
|
|
723
850
|
|
|
724
851
|
// Handle root-level Union types (discriminated unions)
|
|
725
|
-
if (AST.isUnion(ast)) {
|
|
726
|
-
const types = ast.types
|
|
727
|
-
|
|
852
|
+
if (S.AST.isUnion(ast)) {
|
|
728
853
|
// 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)
|
|
854
|
+
const nonNullTypes = getNonNullTypes(ast.types)
|
|
732
855
|
|
|
733
856
|
// Check if this is a discriminated union (all members are structs)
|
|
734
|
-
const allStructs = nonNullTypes.every(
|
|
857
|
+
const allStructs = nonNullTypes.every(S.AST.isObjects)
|
|
735
858
|
|
|
736
859
|
if (allStructs && nonNullTypes.length > 0) {
|
|
737
860
|
// Extract discriminator values from each union member
|
|
@@ -739,16 +862,32 @@ const metadataFromAst = <_From, To>(
|
|
|
739
862
|
|
|
740
863
|
// Store metadata for each union member by its tag value
|
|
741
864
|
for (const memberType of nonNullTypes) {
|
|
742
|
-
if (AST.isObjects(memberType)) {
|
|
865
|
+
if (S.AST.isObjects(memberType)) {
|
|
743
866
|
// Find the discriminator field (usually _tag)
|
|
744
867
|
const tagProp = memberType.propertySignatures.find(
|
|
745
|
-
(p
|
|
868
|
+
(p) => p.name.toString() === "_tag"
|
|
746
869
|
)
|
|
747
870
|
|
|
748
871
|
let tagValue: string | null = null
|
|
749
|
-
|
|
750
|
-
|
|
872
|
+
// TODO: remove after manual _tag deprecation — unwrap legacy S.Struct({ _tag: S.Literal("X") }) pattern
|
|
873
|
+
const resolvedTagType = tagProp ? unwrapSingleLiteralUnion(tagProp.type) : null
|
|
874
|
+
if (resolvedTagType && S.AST.isLiteral(resolvedTagType)) {
|
|
875
|
+
tagValue = resolvedTagType.literal as string
|
|
751
876
|
discriminatorValues.push(tagValue)
|
|
877
|
+
// Warn if the tag was wrapped in a single-element Union (legacy pattern)
|
|
878
|
+
if (
|
|
879
|
+
tagProp
|
|
880
|
+
&& S.AST.isUnion(tagProp.type)
|
|
881
|
+
&& isDevelopmentEnvironment()
|
|
882
|
+
&& tagValue != null
|
|
883
|
+
&& !legacyTagWarningEmittedFor.has(tagValue)
|
|
884
|
+
) {
|
|
885
|
+
legacyTagWarningEmittedFor.add(tagValue)
|
|
886
|
+
console.warn(
|
|
887
|
+
`[OmegaForm] Union member with _tag "${tagValue}" uses S.Struct({ _tag: S.Literal("${tagValue}"), ... }). `
|
|
888
|
+
+ `Please migrate to S.TaggedStruct("${tagValue}", { ... }) for cleaner AST handling.`
|
|
889
|
+
)
|
|
890
|
+
}
|
|
752
891
|
}
|
|
753
892
|
|
|
754
893
|
// Create metadata for this member's properties
|
|
@@ -779,7 +918,7 @@ const metadataFromAst = <_From, To>(
|
|
|
779
918
|
}
|
|
780
919
|
}
|
|
781
920
|
|
|
782
|
-
if (AST.isObjects(ast)) {
|
|
921
|
+
if (S.AST.isObjects(ast)) {
|
|
783
922
|
const meta = createMeta<To>({
|
|
784
923
|
propertySignatures: ast.propertySignatures
|
|
785
924
|
})
|
|
@@ -808,16 +947,80 @@ const metadataFromAst = <_From, To>(
|
|
|
808
947
|
return { meta: newMeta, defaultValues, unionMeta }
|
|
809
948
|
}
|
|
810
949
|
|
|
950
|
+
/*
|
|
951
|
+
* Checks if an AST node is a S.Redacted Declaration without encoding.
|
|
952
|
+
* These need to be swapped to S.RedactedFromValue for form usage
|
|
953
|
+
* because S.Redacted expects Redacted objects, not plain strings.
|
|
954
|
+
*/
|
|
955
|
+
const isRedactedWithoutEncoding = (ast: S.AST.AST): boolean =>
|
|
956
|
+
S.AST.isDeclaration(ast)
|
|
957
|
+
&& (ast.annotations as any)?.typeConstructor?._tag === "effect/Redacted"
|
|
958
|
+
&& !ast.encoding
|
|
959
|
+
|
|
960
|
+
/*
|
|
961
|
+
* Creates a form-compatible schema by replacing S.Redacted(X) with
|
|
962
|
+
* S.RedactedFromValue(X). S.Redacted is a Declaration that expects
|
|
963
|
+
* Redacted<A> on both encoded and type sides, so form inputs (which
|
|
964
|
+
* produce plain strings) fail validation. S.RedactedFromValue accepts
|
|
965
|
+
* plain values on the encoded side and wraps them in Redacted on decode.
|
|
966
|
+
*/
|
|
967
|
+
export const toFormSchema = <From, To>(
|
|
968
|
+
schema: S.Codec<To, From, never>
|
|
969
|
+
): S.Codec<To, From, never> => {
|
|
970
|
+
const ast = schema.ast
|
|
971
|
+
const objAst = S.AST.isObjects(ast)
|
|
972
|
+
? ast
|
|
973
|
+
: S.AST.isDeclaration(ast)
|
|
974
|
+
? S.AST.toEncoded(ast)
|
|
975
|
+
: null
|
|
976
|
+
|
|
977
|
+
if (!objAst || !("propertySignatures" in objAst)) return schema
|
|
978
|
+
|
|
979
|
+
let hasRedacted = false
|
|
980
|
+
const props: Record<string, S.Struct.Fields[string]> = {}
|
|
981
|
+
|
|
982
|
+
for (const p of objAst.propertySignatures) {
|
|
983
|
+
if (isRedactedWithoutEncoding(p.type)) {
|
|
984
|
+
hasRedacted = true
|
|
985
|
+
const innerSchema = S.make((p.type as S.AST.Declaration).typeParameters[0]!)
|
|
986
|
+
props[p.name as string] = S.RedactedFromValue(innerSchema)
|
|
987
|
+
} else if (S.AST.isUnion(p.type)) {
|
|
988
|
+
const types = p.type.types
|
|
989
|
+
const redactedType = types.find(isRedactedWithoutEncoding)
|
|
990
|
+
if (redactedType) {
|
|
991
|
+
hasRedacted = true
|
|
992
|
+
const innerSchema = S.make((redactedType as S.AST.Declaration).typeParameters[0]!)
|
|
993
|
+
const hasNull = types.some(S.AST.isNull)
|
|
994
|
+
const hasUndefined = types.some(S.AST.isUndefined)
|
|
995
|
+
const base = S.RedactedFromValue(innerSchema)
|
|
996
|
+
props[p.name as string] = hasNull && hasUndefined
|
|
997
|
+
? S.NullishOr(base)
|
|
998
|
+
: hasNull
|
|
999
|
+
? S.NullOr(base)
|
|
1000
|
+
: hasUndefined
|
|
1001
|
+
? S.UndefinedOr(base)
|
|
1002
|
+
: base
|
|
1003
|
+
} else {
|
|
1004
|
+
props[p.name as string] = S.make(p.type)
|
|
1005
|
+
}
|
|
1006
|
+
} else {
|
|
1007
|
+
props[p.name as string] = S.make(p.type)
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
return hasRedacted ? S.Struct(props) as unknown as S.Codec<To, From, never> : schema
|
|
1012
|
+
}
|
|
1013
|
+
|
|
811
1014
|
export const duplicateSchema = <From, To>(
|
|
812
1015
|
schema: S.Codec<To, From, never>
|
|
813
1016
|
) => {
|
|
814
1017
|
return schema
|
|
815
1018
|
}
|
|
816
1019
|
|
|
817
|
-
export const generateMetaFromSchema = <
|
|
818
|
-
schema:
|
|
1020
|
+
export const generateMetaFromSchema = <From, To>(
|
|
1021
|
+
schema: S.Codec<To, From, never>
|
|
819
1022
|
): {
|
|
820
|
-
schema:
|
|
1023
|
+
schema: S.Codec<To, From, never>
|
|
821
1024
|
meta: MetaRecord<To>
|
|
822
1025
|
unionMeta: Record<string, MetaRecord<To>>
|
|
823
1026
|
} => {
|
|
@@ -833,168 +1036,138 @@ export const generateInputStandardSchemaFromFieldMeta = (
|
|
|
833
1036
|
if (!trans) {
|
|
834
1037
|
trans = useIntl().trans
|
|
835
1038
|
}
|
|
836
|
-
let schema:
|
|
837
|
-
|
|
1039
|
+
let schema: any
|
|
838
1040
|
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
|
-
}
|
|
1041
|
+
case "string":
|
|
1042
|
+
schema = meta.format === "email"
|
|
1043
|
+
? S.Email.annotate({
|
|
1044
|
+
message: trans("validation.email.invalid")
|
|
1045
|
+
})
|
|
1046
|
+
: S.String.annotate({
|
|
1047
|
+
message: trans("validation.empty")
|
|
1048
|
+
})
|
|
852
1049
|
|
|
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
|
-
}
|
|
1050
|
+
if (meta.required) {
|
|
1051
|
+
schema = schema.check(S.isMinLength(1, {
|
|
1052
|
+
message: trans("validation.empty")
|
|
1053
|
+
}))
|
|
864
1054
|
}
|
|
865
1055
|
|
|
866
1056
|
if (typeof meta.maxLength === "number") {
|
|
867
|
-
schema = schema.check(
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1057
|
+
schema = schema.check(S.isMaxLength(meta.maxLength, {
|
|
1058
|
+
message: trans("validation.string.maxLength", {
|
|
1059
|
+
maxLength: meta.maxLength
|
|
1060
|
+
})
|
|
1061
|
+
}))
|
|
1062
|
+
}
|
|
1063
|
+
if (typeof meta.minLength === "number") {
|
|
1064
|
+
schema = schema.check(S.isMinLength(meta.minLength, {
|
|
1065
|
+
message: trans("validation.string.minLength", {
|
|
1066
|
+
minLength: meta.minLength
|
|
1067
|
+
})
|
|
1068
|
+
}))
|
|
873
1069
|
}
|
|
874
1070
|
break
|
|
875
|
-
}
|
|
876
1071
|
|
|
877
|
-
case "number":
|
|
1072
|
+
case "number":
|
|
878
1073
|
if (meta.refinement === "int") {
|
|
879
|
-
schema = S
|
|
1074
|
+
schema = S
|
|
1075
|
+
.Number
|
|
1076
|
+
.annotate({
|
|
1077
|
+
message: trans("validation.empty")
|
|
1078
|
+
})
|
|
1079
|
+
.check(S.isInt({
|
|
1080
|
+
message: trans("validation.integer.expected", { actualValue: "NaN" })
|
|
1081
|
+
}))
|
|
880
1082
|
} else {
|
|
881
|
-
schema = S.
|
|
1083
|
+
schema = S.Finite.annotate({
|
|
1084
|
+
message: trans("validation.number.expected", { actualValue: "NaN" })
|
|
1085
|
+
})
|
|
1086
|
+
|
|
1087
|
+
if (meta.required) {
|
|
1088
|
+
schema = schema.annotate({
|
|
1089
|
+
message: trans("validation.empty")
|
|
1090
|
+
})
|
|
1091
|
+
}
|
|
882
1092
|
}
|
|
883
1093
|
|
|
884
|
-
// Apply numeric validations
|
|
885
1094
|
if (typeof meta.minimum === "number") {
|
|
886
|
-
schema = schema.check(
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
),
|
|
893
|
-
{ title: `>=${meta.minimum}` }
|
|
894
|
-
)
|
|
895
|
-
)
|
|
1095
|
+
schema = schema.check(S.isGreaterThanOrEqualTo(meta.minimum, {
|
|
1096
|
+
message: trans(meta.minimum === 0 ? "validation.number.positive" : "validation.number.min", {
|
|
1097
|
+
minimum: meta.minimum,
|
|
1098
|
+
isExclusive: true
|
|
1099
|
+
})
|
|
1100
|
+
}))
|
|
896
1101
|
}
|
|
897
|
-
|
|
898
1102
|
if (typeof meta.maximum === "number") {
|
|
899
|
-
schema = schema.check(
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}),
|
|
906
|
-
{ title: `<=${meta.maximum}` }
|
|
907
|
-
)
|
|
908
|
-
)
|
|
1103
|
+
schema = schema.check(S.isLessThanOrEqualTo(meta.maximum, {
|
|
1104
|
+
message: trans("validation.number.max", {
|
|
1105
|
+
maximum: meta.maximum,
|
|
1106
|
+
isExclusive: true
|
|
1107
|
+
})
|
|
1108
|
+
}))
|
|
909
1109
|
}
|
|
910
|
-
|
|
911
1110
|
if (typeof meta.exclusiveMinimum === "number") {
|
|
912
|
-
schema = schema.check(
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
),
|
|
919
|
-
{ title: `>${meta.exclusiveMinimum}` }
|
|
920
|
-
)
|
|
921
|
-
)
|
|
1111
|
+
schema = schema.check(S.isGreaterThan(meta.exclusiveMinimum, {
|
|
1112
|
+
message: trans(meta.exclusiveMinimum === 0 ? "validation.number.positive" : "validation.number.min", {
|
|
1113
|
+
minimum: meta.exclusiveMinimum,
|
|
1114
|
+
isExclusive: false
|
|
1115
|
+
})
|
|
1116
|
+
}))
|
|
922
1117
|
}
|
|
923
|
-
|
|
924
1118
|
if (typeof meta.exclusiveMaximum === "number") {
|
|
925
|
-
schema = schema.check(
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}),
|
|
932
|
-
{ title: `<${meta.exclusiveMaximum}` }
|
|
933
|
-
)
|
|
934
|
-
)
|
|
1119
|
+
schema = schema.check(S.isLessThan(meta.exclusiveMaximum, {
|
|
1120
|
+
message: trans("validation.number.max", {
|
|
1121
|
+
maximum: meta.exclusiveMaximum,
|
|
1122
|
+
isExclusive: false
|
|
1123
|
+
})
|
|
1124
|
+
}))
|
|
935
1125
|
}
|
|
936
1126
|
break
|
|
937
|
-
|
|
1127
|
+
case "select":
|
|
1128
|
+
schema = S.Literals(meta.members as [any, ...any[]]).annotate({
|
|
1129
|
+
message: trans("validation.not_a_valid", {
|
|
1130
|
+
type: "select",
|
|
1131
|
+
message: meta.members.join(", ")
|
|
1132
|
+
})
|
|
1133
|
+
})
|
|
938
1134
|
|
|
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
1135
|
break
|
|
950
|
-
}
|
|
951
1136
|
|
|
952
|
-
case "multiple":
|
|
953
|
-
schema = S.Array(S.String)
|
|
1137
|
+
case "multiple":
|
|
1138
|
+
schema = S.Array(S.String).annotate({
|
|
1139
|
+
message: trans("validation.not_a_valid", {
|
|
1140
|
+
type: "multiple",
|
|
1141
|
+
message: meta.members.join(", ")
|
|
1142
|
+
})
|
|
1143
|
+
})
|
|
954
1144
|
break
|
|
955
|
-
}
|
|
956
1145
|
|
|
957
|
-
case "boolean":
|
|
1146
|
+
case "boolean":
|
|
958
1147
|
schema = S.Boolean
|
|
959
1148
|
break
|
|
960
|
-
}
|
|
961
1149
|
|
|
962
|
-
case "
|
|
1150
|
+
case "date":
|
|
1151
|
+
schema = S.Date
|
|
1152
|
+
break
|
|
1153
|
+
|
|
1154
|
+
case "unknown":
|
|
963
1155
|
schema = S.Unknown
|
|
964
1156
|
break
|
|
965
|
-
}
|
|
966
1157
|
|
|
967
|
-
default:
|
|
968
|
-
|
|
1158
|
+
default:
|
|
1159
|
+
// For any unhandled types, use Unknown schema to prevent undefined errors
|
|
1160
|
+
console.warn(`Unhandled field type: ${meta}`)
|
|
969
1161
|
schema = S.Unknown
|
|
970
1162
|
break
|
|
971
|
-
}
|
|
972
1163
|
}
|
|
973
|
-
|
|
974
|
-
// Wrap in union with null/undefined if not required
|
|
975
1164
|
if (!meta.required) {
|
|
976
|
-
|
|
977
|
-
schema = S.Union([schema, S.Null, S.Undefined])
|
|
1165
|
+
schema = S.NullishOr(schema)
|
|
978
1166
|
}
|
|
979
|
-
|
|
980
|
-
return
|
|
1167
|
+
const result = S.toStandardSchemaV1(schema as any)
|
|
1168
|
+
return result
|
|
981
1169
|
}
|
|
982
1170
|
|
|
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
1171
|
export type OmegaAutoGenMeta<
|
|
999
1172
|
From extends Record<PropertyKey, any>,
|
|
1000
1173
|
To extends Record<PropertyKey, any>,
|
|
@@ -1037,18 +1210,8 @@ export function deepMerge(target: any, source: any) {
|
|
|
1037
1210
|
return result
|
|
1038
1211
|
}
|
|
1039
1212
|
|
|
1040
|
-
// Type definitions for schemas with fields and members
|
|
1041
|
-
type SchemaWithFields = {
|
|
1042
|
-
fields: Record<string, S.Top>
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
1213
|
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"
|
|
1214
|
+
members: readonly S.Schema<any>[]
|
|
1052
1215
|
}
|
|
1053
1216
|
|
|
1054
1217
|
function hasMembers(schema: any): schema is SchemaWithMembers {
|
|
@@ -1057,10 +1220,15 @@ function hasMembers(schema: any): schema is SchemaWithMembers {
|
|
|
1057
1220
|
|
|
1058
1221
|
// Internal implementation with WeakSet tracking
|
|
1059
1222
|
export const defaultsValueFromSchema = (
|
|
1060
|
-
schema: S.
|
|
1223
|
+
schema: S.Schema<any>,
|
|
1061
1224
|
record: Record<string, any> = {}
|
|
1062
1225
|
): any => {
|
|
1063
1226
|
const ast = schema.ast
|
|
1227
|
+
const defaultValue = getDefaultFromAst(ast)
|
|
1228
|
+
|
|
1229
|
+
if (defaultValue !== undefined) {
|
|
1230
|
+
return defaultValue
|
|
1231
|
+
}
|
|
1064
1232
|
|
|
1065
1233
|
if (isNullableOrUndefined(schema.ast) === "null") {
|
|
1066
1234
|
return null
|
|
@@ -1069,106 +1237,79 @@ export const defaultsValueFromSchema = (
|
|
|
1069
1237
|
return undefined
|
|
1070
1238
|
}
|
|
1071
1239
|
|
|
1072
|
-
// Handle
|
|
1073
|
-
|
|
1074
|
-
|
|
1240
|
+
// Handle structs via AST (covers plain structs, transformed schemas like decodeTo, ExtendedClass, etc.)
|
|
1241
|
+
const objectsAst = S.AST.isObjects(ast)
|
|
1242
|
+
? ast
|
|
1243
|
+
: S.AST.isDeclaration(ast)
|
|
1244
|
+
? unwrapDeclaration(ast)
|
|
1245
|
+
: undefined
|
|
1246
|
+
if (objectsAst && S.AST.isObjects(objectsAst)) {
|
|
1247
|
+
const result: Record<string, any> = {}
|
|
1075
1248
|
|
|
1076
|
-
for (const prop of
|
|
1249
|
+
for (const prop of objectsAst.propertySignatures) {
|
|
1077
1250
|
const key = prop.name.toString()
|
|
1078
1251
|
const propType = prop.type
|
|
1079
1252
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
propSchema = (schema as any).fields[key]
|
|
1085
|
-
} else {
|
|
1086
|
-
propSchema = S.make(propType)
|
|
1253
|
+
const propDefault = getDefaultFromAst(propType)
|
|
1254
|
+
if (propDefault !== undefined) {
|
|
1255
|
+
result[key] = propDefault
|
|
1256
|
+
continue
|
|
1087
1257
|
}
|
|
1088
1258
|
|
|
1089
|
-
|
|
1259
|
+
const propSchema = S.make(propType)
|
|
1090
1260
|
const propValue = defaultsValueFromSchema(propSchema, record[key] || {})
|
|
1091
1261
|
|
|
1092
1262
|
if (propValue !== undefined) {
|
|
1093
1263
|
result[key] = propValue
|
|
1264
|
+
} else if (isNullableOrUndefined(propType) === "undefined") {
|
|
1265
|
+
result[key] = undefined
|
|
1094
1266
|
}
|
|
1095
1267
|
}
|
|
1096
1268
|
|
|
1097
|
-
return result
|
|
1269
|
+
return { ...result, ...record }
|
|
1098
1270
|
}
|
|
1099
1271
|
|
|
1100
|
-
//
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1272
|
+
// Handle unions via AST or schema-level .members
|
|
1273
|
+
const unionTypes = S.AST.isUnion(ast)
|
|
1274
|
+
? ast.types
|
|
1275
|
+
: hasMembers(schema)
|
|
1276
|
+
? schema.members.map((m) => m.ast)
|
|
1277
|
+
: undefined
|
|
1278
|
+
if (unionTypes) {
|
|
1279
|
+
const mergedFields: Record<string, { ast: S.AST.AST }> = {}
|
|
1280
|
+
|
|
1281
|
+
for (const memberAstRaw of unionTypes) {
|
|
1282
|
+
const memberAst = unwrapDeclaration(memberAstRaw)
|
|
1283
|
+
if (!S.AST.isObjects(memberAst)) continue
|
|
1284
|
+
|
|
1285
|
+
for (const prop of memberAst.propertySignatures) {
|
|
1286
|
+
const key = prop.name.toString()
|
|
1287
|
+
const fieldDefault = getDefaultFromAst(prop.type)
|
|
1288
|
+
const existingDefault = mergedFields[key] ? getDefaultFromAst(mergedFields[key]!.ast) : undefined
|
|
1289
|
+
|
|
1290
|
+
if (!mergedFields[key] || (fieldDefault !== undefined && existingDefault === undefined)) {
|
|
1291
|
+
mergedFields[key] = { ast: prop.type }
|
|
1114
1292
|
}
|
|
1115
1293
|
}
|
|
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
1294
|
}
|
|
1123
1295
|
|
|
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>)
|
|
1296
|
+
if (Object.keys(mergedFields).length === 0) {
|
|
1297
|
+
return Object.keys(record).length > 0 ? record : undefined
|
|
1298
|
+
}
|
|
1151
1299
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
acc[key] = defaultsValueFromSchema(value, record[key] || {})
|
|
1300
|
+
return Object.entries(mergedFields).reduce((acc, [key, { ast: propAst }]) => {
|
|
1301
|
+
acc[key] = defaultsValueFromSchema(S.make(propAst), record[key] || {})
|
|
1155
1302
|
return acc
|
|
1156
1303
|
}, record)
|
|
1157
1304
|
}
|
|
1158
1305
|
|
|
1159
1306
|
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
|
|
1307
|
+
if (S.AST.isString(ast)) {
|
|
1308
|
+
return ""
|
|
1165
1309
|
}
|
|
1166
|
-
}
|
|
1167
1310
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
if (AST.isBoolean(ast)) {
|
|
1172
|
-
return false
|
|
1311
|
+
if (S.AST.isBoolean(ast)) {
|
|
1312
|
+
return false
|
|
1313
|
+
}
|
|
1173
1314
|
}
|
|
1174
1315
|
}
|