@effect-app/vue 4.0.0-beta.34 → 4.0.0-beta.36
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/CHANGELOG.md +12 -0
- package/dist/makeClient.d.ts +7 -7
- package/package.json +2 -2
- package/test/dist/stubs.d.ts +8 -8
- package/test/form-validation-errors.test.ts +0 -158
- package/test/form.test.ts +0 -261
package/CHANGELOG.md
CHANGED
package/dist/makeClient.d.ts
CHANGED
|
@@ -402,14 +402,14 @@ export declare class QueryImpl<R> {
|
|
|
402
402
|
type Base = I18n | Toast;
|
|
403
403
|
type Mix = ApiClientFactory | Commander | LegacyMutation | Base;
|
|
404
404
|
export declare const makeClient: <RT_, RTHooks>(getBaseMrt: () => ManagedRuntime.ManagedRuntime<RT_ | Mix, never>, clientFor_: ReturnType<(typeof ApiClientFactory)["makeFor"]>, rtHooks: Layer.Layer<RTHooks, never, Mix>) => {
|
|
405
|
-
Command: CommanderResolved<
|
|
406
|
-
useCommand: () => CommanderResolved<
|
|
407
|
-
clientFor: <M extends Requests>(m: M, queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>) => { [Key in keyof RequestHandlers<never, never, M, M["meta"]["moduleName"]>]: RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key] & RequestWithExtensions<RTHooks | (
|
|
408
|
-
mutate: MutationWithExtensions<RTHooks | (
|
|
409
|
-
} & Queries<
|
|
410
|
-
helpers: { [Key_1 in keyof RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${ToCamel<string & Key_1>}Request`]: RequestWithExtensions<RTHooks | (
|
|
405
|
+
Command: CommanderResolved<Mix | RT_, RTHooks>;
|
|
406
|
+
useCommand: () => CommanderResolved<Mix | RT_, RTHooks>;
|
|
407
|
+
clientFor: <M extends Requests>(m: M, queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>) => { [Key in keyof RequestHandlers<never, never, M, M["meta"]["moduleName"]>]: RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key] & RequestWithExtensions<RTHooks | (Mix | RT_), RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key]> & {
|
|
408
|
+
mutate: MutationWithExtensions<RTHooks | (Mix | RT_), RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key]>;
|
|
409
|
+
} & Queries<Mix | RT_, RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key]>; } & {
|
|
410
|
+
helpers: { [Key_1 in keyof RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${ToCamel<string & Key_1>}Request`]: RequestWithExtensions<RTHooks | (Mix | RT_), RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_1]>; } & { [Key_2 in keyof RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${ToCamel<string & Key_2>}Mutation`]: MutationWithExtensions<RTHooks | (Mix | RT_), RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_2]>; } & { [Key_3 in keyof RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${ToCamel<string & Key_3>}Query`]: Queries<Mix | RT_, RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_3]>["query"]; } & { [Key_4 in keyof RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${ToCamel<string & Key_4>}SuspenseQuery`]: Queries<Mix | RT_, RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_4]>["suspense"]; };
|
|
411
411
|
};
|
|
412
|
-
legacy: Legacy<
|
|
412
|
+
legacy: Legacy<Mix | RT_>;
|
|
413
413
|
};
|
|
414
414
|
export interface Legacy<R> extends Pick<QueryImpl<R>, "useQuery" | "useSuspenseQuery">, Omit<LegacyMutationImpl<R>, "getRuntime" | "toast" | "intl"> {
|
|
415
415
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/vue",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.36",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/effect-ts-app/libs/tree/main/packages/vue",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@vueuse/core": "^14.2.1",
|
|
12
12
|
"change-case": "^5.4.4",
|
|
13
13
|
"query-string": "^9.3.1",
|
|
14
|
-
"effect-app": "4.0.0-beta.
|
|
14
|
+
"effect-app": "4.0.0-beta.36"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
17
|
"@effect/atom-vue": "^4.0.0-beta.40",
|
package/test/dist/stubs.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare const fakeIntlLayer: (messages?: Record<string, string> | Record<
|
|
|
13
13
|
export declare const useExperimental: (options?: {
|
|
14
14
|
messages?: Record<string, string> | Record<string, MessageFormatElement[]>;
|
|
15
15
|
toasts: any[];
|
|
16
|
-
}) => import("../src/experimental/makeUseCommand.js").CommanderResolved<
|
|
16
|
+
}) => import("../src/experimental/makeUseCommand.js").CommanderResolved<Toast.Toast | I18n | WithToast, never>;
|
|
17
17
|
declare const RequestContextMap_base: (new () => {
|
|
18
18
|
readonly config: {};
|
|
19
19
|
}) & {
|
|
@@ -155,14 +155,14 @@ export declare const useClient: (options?: {
|
|
|
155
155
|
messages?: Record<string, string> | Record<string, MessageFormatElement[]>;
|
|
156
156
|
toasts: any[];
|
|
157
157
|
}) => {
|
|
158
|
-
Command: import("../src/experimental/makeUseCommand.js").CommanderResolved<WithToast | (Commander | ApiClientFactory | LegacyMutation | (
|
|
159
|
-
useCommand: () => import("../src/experimental/makeUseCommand.js").CommanderResolved<WithToast | (Commander | ApiClientFactory | LegacyMutation | (
|
|
160
|
-
clientFor: <M extends import("effect-app/client").Requests>(m: M, queryInvalidation?: ((client: import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>) => import("../src/makeClient.js").QueryInvalidation<M>) | undefined) => { [Key in keyof import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>]: import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key] & import("../src/makeClient.js").RequestWithExtensions<WithToast | (Commander | ApiClientFactory | LegacyMutation | (
|
|
161
|
-
mutate: import("../src/makeClient.js").MutationWithExtensions<WithToast | (Commander | ApiClientFactory | LegacyMutation | (
|
|
162
|
-
} & import("../src/makeClient.js").Queries<WithToast | (Commander | ApiClientFactory | LegacyMutation | (
|
|
163
|
-
helpers: { [Key_1 in keyof import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${import("../src/makeClient.js").ToCamel<string & Key_1>}Request`]: import("../src/makeClient.js").RequestWithExtensions<WithToast | (Commander | ApiClientFactory | LegacyMutation | (
|
|
158
|
+
Command: import("../src/experimental/makeUseCommand.js").CommanderResolved<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), never>;
|
|
159
|
+
useCommand: () => import("../src/experimental/makeUseCommand.js").CommanderResolved<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), never>;
|
|
160
|
+
clientFor: <M extends import("effect-app/client").Requests>(m: M, queryInvalidation?: ((client: import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>) => import("../src/makeClient.js").QueryInvalidation<M>) | undefined) => { [Key in keyof import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>]: import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key] & import("../src/makeClient.js").RequestWithExtensions<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key]> & {
|
|
161
|
+
mutate: import("../src/makeClient.js").MutationWithExtensions<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key]>;
|
|
162
|
+
} & import("../src/makeClient.js").Queries<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key]>; } & {
|
|
163
|
+
helpers: { [Key_1 in keyof import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${import("../src/makeClient.js").ToCamel<string & Key_1>}Request`]: import("../src/makeClient.js").RequestWithExtensions<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_1]>; } & { [Key_2 in keyof import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${import("../src/makeClient.js").ToCamel<string & Key_2>}Mutation`]: import("../src/makeClient.js").MutationWithExtensions<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_2]>; } & { [Key_3 in keyof import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${import("../src/makeClient.js").ToCamel<string & Key_3>}Query`]: import("../src/makeClient.js").Queries<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_3]>["query"]; } & { [Key_4 in keyof import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]> as `${import("../src/makeClient.js").ToCamel<string & Key_4>}SuspenseQuery`]: import("../src/makeClient.js").Queries<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n)), import("effect-app/client").RequestHandlers<never, never, M, M["meta"]["moduleName"]>[Key_4]>["suspense"]; };
|
|
164
164
|
};
|
|
165
|
-
legacy: import("../src/makeClient.js").Legacy<WithToast | (Commander | ApiClientFactory | LegacyMutation | (
|
|
165
|
+
legacy: import("../src/makeClient.js").Legacy<WithToast | (Commander | ApiClientFactory | LegacyMutation | (Toast.Toast | I18n))>;
|
|
166
166
|
};
|
|
167
167
|
export {};
|
|
168
168
|
//# sourceMappingURL=stubs.d.ts.map
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { Effect, S } from "effect-app"
|
|
2
|
-
import { buildFieldInfoFromFieldsRoot, translate } from "../src/form.js"
|
|
3
|
-
|
|
4
|
-
// test schema with integer field
|
|
5
|
-
class TestSchema extends S.Class<TestSchema>("TestSchema")({
|
|
6
|
-
integerField: S.Int,
|
|
7
|
-
numberField: S.Finite,
|
|
8
|
-
stringField: S.String
|
|
9
|
-
}) {}
|
|
10
|
-
|
|
11
|
-
// translation dictionaries
|
|
12
|
-
const translations: Record<string, Record<string, string>> = {
|
|
13
|
-
en: {
|
|
14
|
-
"validation.integer.expected": "Expected an integer, actual {actualValue}",
|
|
15
|
-
"validation.number.expected": "Expected a number, actual {actualValue}"
|
|
16
|
-
},
|
|
17
|
-
de: {
|
|
18
|
-
"validation.integer.expected": "Es wird eine ganze Zahl erwartet, tatsächlich: {actualValue}",
|
|
19
|
-
"validation.number.expected": "Es wird eine Zahl erwartet, tatsächlich: {actualValue}"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// mock translate function to capture translation calls and return actual translated string
|
|
24
|
-
const translationCalls: Array<{ id: string; defaultMessage: string; params?: any }> = []
|
|
25
|
-
let currentLocale = "en"
|
|
26
|
-
|
|
27
|
-
const mockTranslate = (msg: { id: string; defaultMessage: string }, params?: any) => {
|
|
28
|
-
translationCalls.push({ id: msg.id, defaultMessage: msg.defaultMessage, params })
|
|
29
|
-
|
|
30
|
-
// get the translation template
|
|
31
|
-
const template = translations[currentLocale]?.[msg.id] || msg.defaultMessage
|
|
32
|
-
|
|
33
|
-
// replace parameters in the template
|
|
34
|
-
if (params) {
|
|
35
|
-
return Object.entries(params).reduce((result, [key, value]) => {
|
|
36
|
-
return result.replace(`{${key}}`, String(value))
|
|
37
|
-
}, template)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return template
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
translationCalls.length = 0
|
|
45
|
-
currentLocale = "en"
|
|
46
|
-
translate.value = mockTranslate as any
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it("validates integer field with decimal value (English)", () =>
|
|
50
|
-
Effect
|
|
51
|
-
.gen(function*() {
|
|
52
|
-
currentLocale = "en"
|
|
53
|
-
const fieldInfo = buildFieldInfoFromFieldsRoot(TestSchema)
|
|
54
|
-
const integerFieldInfo = fieldInfo.fields.integerField
|
|
55
|
-
|
|
56
|
-
expect(integerFieldInfo._tag).toBe("FieldInfo")
|
|
57
|
-
expect(integerFieldInfo.type).toBe("int")
|
|
58
|
-
|
|
59
|
-
// test validation rules with a decimal value
|
|
60
|
-
const result = integerFieldInfo.rules[1] ? integerFieldInfo.rules[1]("59.5") : undefined
|
|
61
|
-
|
|
62
|
-
console.log("Validation result (EN):", result)
|
|
63
|
-
console.log("Translation calls:", translationCalls)
|
|
64
|
-
|
|
65
|
-
// the validation should fail
|
|
66
|
-
expect(result).not.toBe(true)
|
|
67
|
-
expect(typeof result).toBe("string")
|
|
68
|
-
|
|
69
|
-
// check if the correct translation key was called
|
|
70
|
-
const integerErrorCall = translationCalls.find((call) => call.id === "validation.integer.expected")
|
|
71
|
-
expect(integerErrorCall).toBeDefined()
|
|
72
|
-
expect(integerErrorCall?.params).toHaveProperty("actualValue")
|
|
73
|
-
expect(integerErrorCall?.params.actualValue).toBe("59.5")
|
|
74
|
-
|
|
75
|
-
// MOST IMPORTANT: verify the actual translated string returned
|
|
76
|
-
expect(result).toBe("Expected an integer, actual 59.5")
|
|
77
|
-
})
|
|
78
|
-
.pipe(Effect.runPromise))
|
|
79
|
-
|
|
80
|
-
it("validates integer field with decimal value (German)", () =>
|
|
81
|
-
Effect
|
|
82
|
-
.gen(function*() {
|
|
83
|
-
currentLocale = "de"
|
|
84
|
-
const fieldInfo = buildFieldInfoFromFieldsRoot(TestSchema)
|
|
85
|
-
const integerFieldInfo = fieldInfo.fields.integerField
|
|
86
|
-
|
|
87
|
-
expect(integerFieldInfo._tag).toBe("FieldInfo")
|
|
88
|
-
expect(integerFieldInfo.type).toBe("int")
|
|
89
|
-
|
|
90
|
-
// test validation rules with a decimal value
|
|
91
|
-
const result = integerFieldInfo.rules[1] ? integerFieldInfo.rules[1]("59.5") : undefined
|
|
92
|
-
|
|
93
|
-
console.log("Validation result (DE):", result)
|
|
94
|
-
|
|
95
|
-
// the validation should fail
|
|
96
|
-
expect(result).not.toBe(true)
|
|
97
|
-
expect(typeof result).toBe("string")
|
|
98
|
-
|
|
99
|
-
// MOST IMPORTANT: verify the actual German translated string returned
|
|
100
|
-
expect(result).toBe("Es wird eine ganze Zahl erwartet, tatsächlich: 59.5")
|
|
101
|
-
})
|
|
102
|
-
.pipe(Effect.runPromise))
|
|
103
|
-
|
|
104
|
-
it("validates string field parsed as number", () =>
|
|
105
|
-
Effect
|
|
106
|
-
.gen(function*() {
|
|
107
|
-
const fieldInfo = buildFieldInfoFromFieldsRoot(TestSchema)
|
|
108
|
-
const stringFieldInfo = fieldInfo.fields.stringField
|
|
109
|
-
|
|
110
|
-
expect(stringFieldInfo._tag).toBe("FieldInfo")
|
|
111
|
-
expect(stringFieldInfo.type).toBe("text")
|
|
112
|
-
|
|
113
|
-
// test validation rules with a number that should fail string validation
|
|
114
|
-
const result = stringFieldInfo.rules[0] ? stringFieldInfo.rules[0]("123") : undefined
|
|
115
|
-
|
|
116
|
-
console.log("Validation result for string field:", result)
|
|
117
|
-
|
|
118
|
-
// string field should accept "123" as a valid string
|
|
119
|
-
expect(result).toBe(true)
|
|
120
|
-
})
|
|
121
|
-
.pipe(Effect.runPromise))
|
|
122
|
-
|
|
123
|
-
it("validates integer field with valid integer", () =>
|
|
124
|
-
Effect
|
|
125
|
-
.gen(function*() {
|
|
126
|
-
const fieldInfo = buildFieldInfoFromFieldsRoot(TestSchema)
|
|
127
|
-
const integerFieldInfo = fieldInfo.fields.integerField
|
|
128
|
-
|
|
129
|
-
// test validation rules with a valid integer
|
|
130
|
-
const result = integerFieldInfo.rules[1] ? integerFieldInfo.rules[1]("42") : undefined
|
|
131
|
-
|
|
132
|
-
console.log("Validation result for valid integer:", result)
|
|
133
|
-
|
|
134
|
-
// the validation should pass
|
|
135
|
-
expect(result).toBe(true)
|
|
136
|
-
})
|
|
137
|
-
.pipe(Effect.runPromise))
|
|
138
|
-
|
|
139
|
-
it("error message format matches regex pattern", () => {
|
|
140
|
-
// test the actual error message format from Effect Schema
|
|
141
|
-
const errorMessage = `Int
|
|
142
|
-
└─ From side refinement failure
|
|
143
|
-
└─ Int
|
|
144
|
-
└─ Predicate refinement failure
|
|
145
|
-
└─ Expected an integer, actual 59.5`
|
|
146
|
-
|
|
147
|
-
const integerMatch = errorMessage.match(/Expected.*integer.*actual\s+(.+)/i)
|
|
148
|
-
expect(integerMatch).toBeTruthy()
|
|
149
|
-
expect(integerMatch![1]).toBe("59.5")
|
|
150
|
-
|
|
151
|
-
const numberErrorMessage = `Number
|
|
152
|
-
└─ Type side transformation failure
|
|
153
|
-
└─ Expected a number, actual "not-a-number"`
|
|
154
|
-
|
|
155
|
-
const numberMatch = numberErrorMessage.match(/Expected.*number.*actual\s+(.+)/i)
|
|
156
|
-
expect(numberMatch).toBeTruthy()
|
|
157
|
-
expect(numberMatch![1]).toBe("\"not-a-number\"")
|
|
158
|
-
})
|
package/test/form.test.ts
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import { Effect, S } from "effect-app"
|
|
2
|
-
import { buildFieldInfoFromFieldsRoot, type DiscriminatedUnionFieldInfo, type FieldInfo, type NestedFieldInfo, type UnionFieldInfo } from "../src/form.js"
|
|
3
|
-
|
|
4
|
-
export class NestedSchema extends S.Class<NestedSchema>("NestedSchema")({
|
|
5
|
-
shallow: S.String,
|
|
6
|
-
nested: S.Struct({
|
|
7
|
-
deep: S.NonEmptyString,
|
|
8
|
-
nested: S.Struct({
|
|
9
|
-
deepest: S.Finite
|
|
10
|
-
})
|
|
11
|
-
}),
|
|
12
|
-
age: S.Struct({ nfs: S.FiniteFromString.pipe(S.decodeTo(S.PositiveInt)) })
|
|
13
|
-
}) {}
|
|
14
|
-
|
|
15
|
-
export class SchemaContainsClass extends S.Class<SchemaContainsClass>("SchemaContainsClass")({
|
|
16
|
-
inner: NestedSchema
|
|
17
|
-
}) {}
|
|
18
|
-
|
|
19
|
-
export class UnionSchema extends S.Class<UnionSchema>("UnionSchema")({
|
|
20
|
-
generalUnion: S.Union([S.String, S.Struct({ unionNested: NestedSchema })]),
|
|
21
|
-
structsUnion: S.Union([NestedSchema, SchemaContainsClass]),
|
|
22
|
-
optional: S.optional(S.String),
|
|
23
|
-
nullable: S.NullOr(S.String)
|
|
24
|
-
}) {}
|
|
25
|
-
|
|
26
|
-
class Circle extends S.TaggedClass<Circle>()("Circle", {
|
|
27
|
-
radius: S.PositiveInt
|
|
28
|
-
}) {}
|
|
29
|
-
|
|
30
|
-
class Square extends S.TaggedClass<Square>()("Square", {
|
|
31
|
-
sideLength: S.PositiveInt
|
|
32
|
-
}) {}
|
|
33
|
-
|
|
34
|
-
class Triangle extends S.TaggedClass<Triangle>()("Triangle", {
|
|
35
|
-
base: S.PositiveInt,
|
|
36
|
-
height: S.Finite
|
|
37
|
-
}) {}
|
|
38
|
-
|
|
39
|
-
const CircleStruct = S.Struct({
|
|
40
|
-
_tag: S.Literal("CircleStruct"),
|
|
41
|
-
radius: S.PositiveInt
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const SquareStruct = S.Struct({
|
|
45
|
-
_tag: S.Literal("SquareStruct"),
|
|
46
|
-
sideLength: S.PositiveInt
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const TriangleStruct = S.Struct({
|
|
50
|
-
_tag: S.Literal("TriangleStruct"),
|
|
51
|
-
base: S.PositiveInt,
|
|
52
|
-
height: S.Finite
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
const ShapeWithStructs = S.Union([CircleStruct, SquareStruct, TriangleStruct])
|
|
56
|
-
const ShapeWithClasses = S.Union([Circle, Square, Triangle])
|
|
57
|
-
|
|
58
|
-
export class ShapeContainer extends S.Class<ShapeContainer>("ShapeContainer")({
|
|
59
|
-
shapeWithStruct: ShapeWithStructs,
|
|
60
|
-
shapeWithClasses: ShapeWithClasses
|
|
61
|
-
}) {}
|
|
62
|
-
|
|
63
|
-
function testFieldInfo<T>(fi: FieldInfo<T>) {
|
|
64
|
-
expect(fi).toBeInstanceOf(Object)
|
|
65
|
-
expect(fi._tag).toBe("FieldInfo")
|
|
66
|
-
expect(["text", "float", "int"]).toContain(fi.type)
|
|
67
|
-
expect(fi.rules).toBeInstanceOf(Array)
|
|
68
|
-
fi.rules.forEach((r) => {
|
|
69
|
-
expect(r).toBeInstanceOf(Function)
|
|
70
|
-
})
|
|
71
|
-
expect(fi.metadata).toBeInstanceOf(Object)
|
|
72
|
-
expect(fi.metadata.maxLength === void 0 || typeof fi.metadata.maxLength === "number").toBeTruthy()
|
|
73
|
-
expect(fi.metadata.minLength === void 0 || typeof fi.metadata.minLength === "number").toBeTruthy()
|
|
74
|
-
expect(typeof fi.metadata.required === "boolean").toBeTruthy()
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function testUnionFieldInfo<T>(ufi: UnionFieldInfo<T[]>) {
|
|
78
|
-
expect(ufi).toBeInstanceOf(Object)
|
|
79
|
-
expect(ufi._tag).toBe("UnionFieldInfo")
|
|
80
|
-
expect(ufi.members).toBeInstanceOf(Array)
|
|
81
|
-
ufi.members.forEach(
|
|
82
|
-
(
|
|
83
|
-
i: any
|
|
84
|
-
) => {
|
|
85
|
-
switch (i._tag) {
|
|
86
|
-
case "FieldInfo":
|
|
87
|
-
testFieldInfo(i as FieldInfo<any>)
|
|
88
|
-
break
|
|
89
|
-
case "NestedFieldInfo":
|
|
90
|
-
testNestedFieldInfo(i as NestedFieldInfo<any>)
|
|
91
|
-
break
|
|
92
|
-
case "UnionFieldInfo":
|
|
93
|
-
testUnionFieldInfo(i as UnionFieldInfo<any>)
|
|
94
|
-
break
|
|
95
|
-
case "DiscriminatedUnionFieldInfo":
|
|
96
|
-
testDiscriminatedUnionFieldInfo(i as DiscriminatedUnionFieldInfo<any>)
|
|
97
|
-
break
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function testNestedFieldInfo<T extends Record<PropertyKey, any>>(nfi: NestedFieldInfo<T>) {
|
|
104
|
-
expect(nfi).toBeInstanceOf(Object)
|
|
105
|
-
expect(nfi._tag).toBe("NestedFieldInfo")
|
|
106
|
-
expect(nfi.fields).toBeInstanceOf(Object)
|
|
107
|
-
|
|
108
|
-
// remove the value of _infoTag from the object when it is undefined
|
|
109
|
-
// when it isn't undefined, the followin switch will ignore it
|
|
110
|
-
Object.values(nfi).filter(Boolean).forEach(
|
|
111
|
-
(
|
|
112
|
-
i: any
|
|
113
|
-
) => {
|
|
114
|
-
switch (i._tag) {
|
|
115
|
-
case "FieldInfo":
|
|
116
|
-
testFieldInfo(i as FieldInfo<any>)
|
|
117
|
-
break
|
|
118
|
-
case "NestedFieldInfo":
|
|
119
|
-
testNestedFieldInfo(i as NestedFieldInfo<any>)
|
|
120
|
-
break
|
|
121
|
-
case "UnionFieldInfo":
|
|
122
|
-
testUnionFieldInfo(i as UnionFieldInfo<any>)
|
|
123
|
-
break
|
|
124
|
-
case "DiscriminatedUnionFieldInfo":
|
|
125
|
-
testDiscriminatedUnionFieldInfo(i as DiscriminatedUnionFieldInfo<any>)
|
|
126
|
-
break
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function testDiscriminatedUnionFieldInfo<T extends Record<PropertyKey, any>>(dufi: DiscriminatedUnionFieldInfo<T>) {
|
|
133
|
-
expect(dufi).toBeInstanceOf(Object)
|
|
134
|
-
expect(dufi._tag).toBe("DiscriminatedUnionFieldInfo")
|
|
135
|
-
expect(dufi.members).toBeInstanceOf(Object)
|
|
136
|
-
|
|
137
|
-
Object.values(dufi.members).forEach(
|
|
138
|
-
(
|
|
139
|
-
i: any
|
|
140
|
-
) => {
|
|
141
|
-
switch (i._tag) {
|
|
142
|
-
case "FieldInfo":
|
|
143
|
-
testFieldInfo(i as FieldInfo<any>)
|
|
144
|
-
break
|
|
145
|
-
case "NestedFieldInfo":
|
|
146
|
-
testNestedFieldInfo(i as NestedFieldInfo<any>)
|
|
147
|
-
break
|
|
148
|
-
case "UnionFieldInfo":
|
|
149
|
-
testUnionFieldInfo(i as UnionFieldInfo<any>)
|
|
150
|
-
break
|
|
151
|
-
case "DiscriminatedUnionFieldInfo":
|
|
152
|
-
testDiscriminatedUnionFieldInfo(i as DiscriminatedUnionFieldInfo<any>)
|
|
153
|
-
break
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
it("buildFieldInfo", () =>
|
|
160
|
-
Effect
|
|
161
|
-
.gen(function*() {
|
|
162
|
-
const nestedFieldinfo = buildFieldInfoFromFieldsRoot(NestedSchema)
|
|
163
|
-
expectTypeOf(nestedFieldinfo).toEqualTypeOf<NestedFieldInfo<NestedSchema>>()
|
|
164
|
-
expectTypeOf(nestedFieldinfo.fields.shallow).toEqualTypeOf<FieldInfo<string>>()
|
|
165
|
-
expectTypeOf(nestedFieldinfo.fields.age).toEqualTypeOf<NestedFieldInfo<NestedSchema["age"]>>()
|
|
166
|
-
// TODO: v4 migration - type inference changed with S.decodeTo, investigate if this is correct
|
|
167
|
-
// expectTypeOf(nestedFieldinfo.fields.age.fields.nfs).toEqualTypeOf<FieldInfo<number & S.PositiveIntBrand>>()
|
|
168
|
-
expectTypeOf(nestedFieldinfo.fields.nested).toEqualTypeOf<NestedFieldInfo<NestedSchema["nested"]>>()
|
|
169
|
-
expectTypeOf(nestedFieldinfo.fields.nested.fields.deep).toEqualTypeOf<FieldInfo<string & S.NonEmptyStringBrand>>()
|
|
170
|
-
expectTypeOf(nestedFieldinfo.fields.nested.fields.nested).toEqualTypeOf<
|
|
171
|
-
NestedFieldInfo<NestedSchema["nested"]["nested"]>
|
|
172
|
-
>()
|
|
173
|
-
expectTypeOf(nestedFieldinfo.fields.nested.fields.nested.fields.deepest).toEqualTypeOf<FieldInfo<number>>()
|
|
174
|
-
|
|
175
|
-
// it's a recursive check on actual runtime structure
|
|
176
|
-
testNestedFieldInfo(nestedFieldinfo)
|
|
177
|
-
testNestedFieldInfo(nestedFieldinfo.fields.nested)
|
|
178
|
-
testNestedFieldInfo(nestedFieldinfo.fields.age)
|
|
179
|
-
})
|
|
180
|
-
.pipe(Effect.runPromise))
|
|
181
|
-
|
|
182
|
-
it("buildFieldInfo schema containing class", () =>
|
|
183
|
-
Effect
|
|
184
|
-
.gen(function*() {
|
|
185
|
-
const fieldinfo = buildFieldInfoFromFieldsRoot(SchemaContainsClass)
|
|
186
|
-
|
|
187
|
-
// the type system says that these are NestedFieldInfo<NestedSchema>s
|
|
188
|
-
// are they really? let's check
|
|
189
|
-
testNestedFieldInfo(fieldinfo.fields.inner)
|
|
190
|
-
testNestedFieldInfo(fieldinfo.fields.inner.fields.nested.fields.nested)
|
|
191
|
-
})
|
|
192
|
-
.pipe(Effect.runPromise))
|
|
193
|
-
|
|
194
|
-
it("buildFieldInfo with simple union", () =>
|
|
195
|
-
Effect
|
|
196
|
-
.gen(function*() {
|
|
197
|
-
const unionFieldinfo = buildFieldInfoFromFieldsRoot(UnionSchema)
|
|
198
|
-
expectTypeOf(unionFieldinfo).toEqualTypeOf<NestedFieldInfo<UnionSchema>>()
|
|
199
|
-
expectTypeOf(unionFieldinfo.fields.nullable).toEqualTypeOf<
|
|
200
|
-
FieldInfo<string | null>
|
|
201
|
-
>()
|
|
202
|
-
expectTypeOf(unionFieldinfo.fields.optional).toEqualTypeOf<
|
|
203
|
-
FieldInfo<string | undefined>
|
|
204
|
-
>()
|
|
205
|
-
expectTypeOf(unionFieldinfo.fields.structsUnion).toEqualTypeOf<
|
|
206
|
-
UnionFieldInfo<(NestedFieldInfo<NestedSchema> | NestedFieldInfo<SchemaContainsClass>)[]>
|
|
207
|
-
>()
|
|
208
|
-
expectTypeOf(unionFieldinfo.fields.generalUnion).toEqualTypeOf<
|
|
209
|
-
FieldInfo<
|
|
210
|
-
string | {
|
|
211
|
-
readonly unionNested: NestedSchema
|
|
212
|
-
}
|
|
213
|
-
>
|
|
214
|
-
>
|
|
215
|
-
|
|
216
|
-
// it's a recursive check on actual runtime structure
|
|
217
|
-
testNestedFieldInfo(unionFieldinfo)
|
|
218
|
-
testFieldInfo(unionFieldinfo.fields.nullable)
|
|
219
|
-
testFieldInfo(unionFieldinfo.fields.optional)
|
|
220
|
-
console.log({ asd: unionFieldinfo.fields.structsUnion })
|
|
221
|
-
testUnionFieldInfo(unionFieldinfo.fields.structsUnion)
|
|
222
|
-
testFieldInfo(unionFieldinfo.fields.generalUnion)
|
|
223
|
-
})
|
|
224
|
-
.pipe(Effect.runPromise))
|
|
225
|
-
|
|
226
|
-
it("buildFieldInfo with tagged unions", () =>
|
|
227
|
-
Effect
|
|
228
|
-
.gen(function*() {
|
|
229
|
-
const shapeFieldinfo = buildFieldInfoFromFieldsRoot(ShapeContainer)
|
|
230
|
-
|
|
231
|
-
// check at runtime if the structure is really an union
|
|
232
|
-
testDiscriminatedUnionFieldInfo(shapeFieldinfo.fields.shapeWithClasses)
|
|
233
|
-
testDiscriminatedUnionFieldInfo(shapeFieldinfo.fields.shapeWithStruct)
|
|
234
|
-
|
|
235
|
-
testNestedFieldInfo(shapeFieldinfo.fields.shapeWithClasses.members.Square)
|
|
236
|
-
expect(shapeFieldinfo.fields.shapeWithClasses.members.Square._infoTag).toBe("Square")
|
|
237
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithClasses.members.Square.fields.sideLength)
|
|
238
|
-
|
|
239
|
-
testNestedFieldInfo(shapeFieldinfo.fields.shapeWithClasses.members.Triangle)
|
|
240
|
-
expect(shapeFieldinfo.fields.shapeWithClasses.members.Triangle._infoTag).toBe("Triangle")
|
|
241
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithClasses.members.Triangle.fields.base)
|
|
242
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithClasses.members.Triangle.fields.height)
|
|
243
|
-
|
|
244
|
-
testNestedFieldInfo(shapeFieldinfo.fields.shapeWithClasses.members.Circle)
|
|
245
|
-
expect(shapeFieldinfo.fields.shapeWithClasses.members.Circle._infoTag).toBe("Circle")
|
|
246
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithClasses.members.Circle.fields.radius)
|
|
247
|
-
|
|
248
|
-
testNestedFieldInfo(shapeFieldinfo.fields.shapeWithStruct.members.SquareStruct)
|
|
249
|
-
expect(shapeFieldinfo.fields.shapeWithStruct.members.SquareStruct._infoTag).toBe("SquareStruct")
|
|
250
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithStruct.members.SquareStruct.fields.sideLength)
|
|
251
|
-
|
|
252
|
-
testNestedFieldInfo(shapeFieldinfo.fields.shapeWithStruct.members.TriangleStruct)
|
|
253
|
-
expect(shapeFieldinfo.fields.shapeWithStruct.members.TriangleStruct._infoTag).toBe("TriangleStruct")
|
|
254
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithStruct.members.TriangleStruct.fields.base)
|
|
255
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithStruct.members.TriangleStruct.fields.height)
|
|
256
|
-
|
|
257
|
-
testNestedFieldInfo(shapeFieldinfo.fields.shapeWithStruct.members.CircleStruct)
|
|
258
|
-
expect(shapeFieldinfo.fields.shapeWithStruct.members.CircleStruct._infoTag).toBe("CircleStruct")
|
|
259
|
-
testFieldInfo(shapeFieldinfo.fields.shapeWithStruct.members.CircleStruct.fields.radius)
|
|
260
|
-
})
|
|
261
|
-
.pipe(Effect.runPromise))
|