effect-app 4.0.0-beta.13 → 4.0.0-beta.131
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 +503 -0
- package/dist/Config/SecretURL.js +2 -2
- package/dist/Config.d.ts +7 -0
- package/dist/Config.d.ts.map +1 -0
- package/dist/Config.js +6 -0
- package/dist/ConfigProvider.d.ts +39 -0
- package/dist/ConfigProvider.d.ts.map +1 -0
- package/dist/ConfigProvider.js +42 -0
- package/dist/{ServiceMap.d.ts → Context.d.ts} +14 -18
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +66 -0
- package/dist/Effect.d.ts +8 -9
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -6
- package/dist/Layer.d.ts +5 -4
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +1 -1
- package/dist/Operations.d.ts +198 -33
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Pure.d.ts +2 -2
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +13 -13
- package/dist/Schema/Class.d.ts +48 -10
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +120 -16
- package/dist/Schema/SpecialJsonSchema.d.ts +33 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +122 -0
- package/dist/Schema/SpecialOpenApi.d.ts +32 -0
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
- package/dist/Schema/SpecialOpenApi.js +123 -0
- package/dist/Schema/brand.d.ts +10 -1
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +1 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +9 -4
- package/dist/Schema/ext.d.ts +112 -47
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +115 -53
- package/dist/Schema/moreStrings.d.ts +110 -10
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +19 -10
- package/dist/Schema/numbers.d.ts +126 -14
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +10 -9
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +8 -3
- package/dist/Schema/strings.d.ts +36 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -1
- package/dist/Schema.d.ts +74 -55
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +85 -64
- package/dist/client/apiClientFactory.d.ts +12 -28
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +16 -17
- package/dist/client/clientFor.d.ts +6 -5
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/errors.d.ts +18 -9
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +35 -10
- package/dist/client/makeClient.d.ts +73 -28
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +49 -23
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -5
- package/dist/ids.d.ts +2 -2
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +3 -2
- package/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -8
- package/dist/middleware.d.ts +2 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +3 -3
- package/dist/rpc/MiddlewareMaker.d.ts +4 -3
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +23 -24
- package/dist/rpc/RpcContextMap.d.ts +2 -2
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +4 -3
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/utils/gen.d.ts +1 -1
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +2 -2
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +3 -3
- package/dist/utils.d.ts +7 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +8 -2
- package/package.json +30 -14
- package/src/Config/SecretURL.ts +1 -1
- package/src/Config.ts +14 -0
- package/src/ConfigProvider.ts +48 -0
- package/src/{ServiceMap.ts → Context.ts} +51 -59
- package/src/Effect.ts +11 -14
- package/src/Layer.ts +5 -4
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +157 -30
- package/src/Schema/SpecialJsonSchema.ts +137 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +18 -3
- package/src/Schema/email.ts +10 -2
- package/src/Schema/ext.ts +196 -87
- package/src/Schema/moreStrings.ts +31 -17
- package/src/Schema/numbers.ts +14 -13
- package/src/Schema/phoneNumber.ts +8 -1
- package/src/Schema/strings.ts +4 -4
- package/src/Schema.ts +195 -104
- package/src/client/apiClientFactory.ts +104 -112
- package/src/client/clientFor.ts +6 -1
- package/src/client/errors.ts +42 -17
- package/src/client/makeClient.ts +150 -61
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +2 -1
- package/src/index.ts +3 -10
- package/src/middleware.ts +2 -2
- package/src/rpc/MiddlewareMaker.ts +33 -44
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +5 -4
- package/src/utils/gen.ts +1 -1
- package/src/utils/logger.ts +2 -2
- package/src/utils.ts +8 -4
- package/test/dist/moreStrings.test.d.ts.map +1 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/secretURL.test.d.ts.map +1 -0
- package/test/dist/special.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +17 -0
- package/test/rpc.test.ts +30 -6
- package/test/schema.test.ts +517 -4
- package/test/secretURL.test.ts +157 -0
- package/test/special.test.ts +862 -0
- package/test/utils.test.ts +2 -2
- package/tsconfig.base.json +0 -1
- package/tsconfig.json +0 -1
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
package/src/Schema/ext.ts
CHANGED
|
@@ -1,56 +1,94 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
|
-
import { Effect, Option, pipe,
|
|
3
|
+
import { Effect, Function, Option, pipe, type SchemaAST, SchemaIssue, SchemaTransformation } from "effect"
|
|
4
4
|
import * as S from "effect/Schema"
|
|
5
|
+
import { isDateValid } from "effect/Schema"
|
|
5
6
|
import { type NonEmptyReadonlyArray } from "../Array.js"
|
|
7
|
+
import * as Context from "../Context.js"
|
|
6
8
|
import { extendM, typedKeysOf } from "../utils.js"
|
|
7
9
|
import { type AST } from "./schema.js"
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
) =>
|
|
16
|
-
<Self extends S.Top>(self: Self): S.withConstructorDefault<Self & S.WithoutConstructorDefault> => {
|
|
17
|
-
type Narrowed = Self & S.WithoutConstructorDefault
|
|
18
|
-
return S.withConstructorDefault<Narrowed>(
|
|
19
|
-
() => Option.some(makeDefault() as Narrowed["~type.make.in"])
|
|
20
|
-
)(self as Narrowed)
|
|
21
|
-
}
|
|
11
|
+
type ProvidedCodec<Self extends S.Top, R> = S.Codec<
|
|
12
|
+
Self["Type"],
|
|
13
|
+
Self["Encoded"],
|
|
14
|
+
Exclude<Self["DecodingServices"], R>,
|
|
15
|
+
Exclude<Self["EncodingServices"], R>
|
|
16
|
+
>
|
|
22
17
|
|
|
23
18
|
// TODO: v4 migration - Date is no longer by default encoded to string.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
19
|
+
|
|
20
|
+
const DateString = S.String.annotate({
|
|
21
|
+
identifier: "Date",
|
|
22
|
+
description: "a string in ISO 8601 format that will be decoded as a Date",
|
|
23
|
+
format: "date-time"
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Schema type for {@link DateFromString}.
|
|
28
|
+
*
|
|
29
|
+
* @category Schemas
|
|
30
|
+
* @since 4.0.0
|
|
31
|
+
*/
|
|
32
|
+
export interface DateFromString extends S.decodeTo<S.Date, S.String> {}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A transformation schema that parses an ISO 8601 string into a `Date`.
|
|
36
|
+
*
|
|
37
|
+
* Decoding:
|
|
38
|
+
* - A `string` is decoded as a `Date`.
|
|
39
|
+
*
|
|
40
|
+
* Encoding:
|
|
41
|
+
* - A `Date` is encoded as a `string`.
|
|
42
|
+
*
|
|
43
|
+
* @since 4.0.0
|
|
44
|
+
*/
|
|
45
|
+
export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString))
|
|
30
46
|
|
|
31
47
|
/**
|
|
32
48
|
* Like the default Schema `Date` but from String with `withDefault` => now
|
|
33
49
|
*/
|
|
34
50
|
export const Date = Object.assign(DateFromString, {
|
|
35
|
-
withDefault: DateFromString.pipe(
|
|
51
|
+
withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
|
|
52
|
+
withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Like the default Schema `DateValid` but from String with `withDefault` => now
|
|
57
|
+
*/
|
|
58
|
+
export const DateValid = Object.assign(Date.check(isDateValid()), {
|
|
59
|
+
withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
|
|
60
|
+
withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
|
|
36
61
|
})
|
|
37
62
|
|
|
38
63
|
/**
|
|
39
64
|
* Like the default Schema `Boolean` but with `withDefault` => false
|
|
40
65
|
*/
|
|
41
66
|
export const Boolean = Object.assign(S.Boolean, {
|
|
42
|
-
withDefault: S.Boolean.pipe(
|
|
67
|
+
withDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
|
|
68
|
+
withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
|
|
43
69
|
})
|
|
44
70
|
|
|
45
71
|
/**
|
|
72
|
+
* You probably want to use `Finite` instead of this.
|
|
46
73
|
* Like the default Schema `Number` but with `withDefault` => 0
|
|
47
74
|
*/
|
|
48
|
-
export const Number = Object.assign(S.Number, {
|
|
75
|
+
export const Number = Object.assign(S.Number, {
|
|
76
|
+
withDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
77
|
+
withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
78
|
+
})
|
|
49
79
|
|
|
50
80
|
/**
|
|
51
|
-
* Like the default Schema `
|
|
81
|
+
* Like the default Schema `Finite` but with `withDefault` => 0
|
|
52
82
|
*/
|
|
53
|
-
export const
|
|
83
|
+
export const Finite = Object.assign(S.Finite, {
|
|
84
|
+
withDefault: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
85
|
+
withDecodingDefaultType: S.Finite.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Like the default Schema `Literals` but with `withDefault` => literals[0]
|
|
90
|
+
*/
|
|
91
|
+
export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
|
|
54
92
|
pipe(
|
|
55
93
|
S.Literals(literals),
|
|
56
94
|
(s) =>
|
|
@@ -58,79 +96,144 @@ export const Literal = <Literals extends NonEmptyReadonlyArray<AST.LiteralValue>
|
|
|
58
96
|
changeDefault: <A extends Literals[number]>(a: A) => {
|
|
59
97
|
return Object.assign(S.Literals(literals), {
|
|
60
98
|
Default: a,
|
|
61
|
-
withDefault: s.pipe(
|
|
99
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
|
|
100
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
|
|
62
101
|
}) // todo: copy annotations from original?
|
|
63
102
|
},
|
|
64
|
-
|
|
65
|
-
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- load-bearing: Object.assign widens the field type without it, breaking `expectTypeOf(l.Default).toEqualTypeOf<"a">()` in tests
|
|
104
|
+
Default: literals[0] as Literals[0],
|
|
105
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
|
|
106
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
|
|
66
107
|
})
|
|
67
108
|
)
|
|
68
109
|
|
|
69
110
|
/**
|
|
70
111
|
* Like the default Schema `Array` but with `withDefault` => []
|
|
71
112
|
*/
|
|
72
|
-
|
|
113
|
+
const co = { parseOptions: { concurrency: "unbounded" as const } }
|
|
114
|
+
export { co as concurrencyUnbounded }
|
|
115
|
+
|
|
116
|
+
export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
73
117
|
return pipe(
|
|
74
|
-
S.Array(value),
|
|
75
|
-
(s) =>
|
|
118
|
+
S.Array(value).annotate(co),
|
|
119
|
+
(s) =>
|
|
120
|
+
Object.assign(s, {
|
|
121
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
|
|
122
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
|
|
123
|
+
})
|
|
76
124
|
)
|
|
77
125
|
}
|
|
78
126
|
|
|
79
127
|
/**
|
|
80
|
-
*
|
|
128
|
+
* An annotated `S.Array` of unique items that decodes to a `ReadonlySet`.
|
|
81
129
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
(
|
|
130
|
+
export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
|
|
131
|
+
const from = S
|
|
132
|
+
.Array(value)
|
|
133
|
+
.annotate({ ...co, expected: "an array of unique items that will be decoded as a ReadonlySet" })
|
|
134
|
+
const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<S.Schema.Type<ValueSchema>>>
|
|
135
|
+
const schema = from.pipe(
|
|
136
|
+
S.decodeTo(
|
|
137
|
+
to,
|
|
138
|
+
SchemaTransformation.transform({
|
|
139
|
+
decode: (arr) => new Set(arr) as ReadonlySet<S.Schema.Type<ValueSchema>>,
|
|
140
|
+
encode: (set) => [...set]
|
|
141
|
+
})
|
|
142
|
+
)
|
|
86
143
|
)
|
|
144
|
+
return schema
|
|
87
145
|
}
|
|
88
146
|
|
|
89
|
-
|
|
147
|
+
/**
|
|
148
|
+
* An annotated `S.Array` of key-value tuples that decodes to a `ReadonlyMap`.
|
|
149
|
+
*/
|
|
150
|
+
export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
151
|
+
readonly key: KeySchema
|
|
152
|
+
readonly value: ValueSchema
|
|
153
|
+
}) => {
|
|
154
|
+
const from = S
|
|
155
|
+
.Array(S.Tuple([pair.key, pair.value]))
|
|
156
|
+
.annotate({ ...co, expected: "an array of key-value tuples that will be decoded as a ReadonlyMap" })
|
|
157
|
+
const to = S.instanceOf(Map) as S.instanceOf<
|
|
158
|
+
ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>
|
|
159
|
+
>
|
|
160
|
+
const schema = from.pipe(
|
|
161
|
+
S.decodeTo(
|
|
162
|
+
to,
|
|
163
|
+
SchemaTransformation.transform({
|
|
164
|
+
decode: (
|
|
165
|
+
arr
|
|
166
|
+
) => new Map(arr) as ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>,
|
|
167
|
+
encode: (
|
|
168
|
+
map
|
|
169
|
+
) => [...map.entries()] as any // fu
|
|
170
|
+
})
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
return schema
|
|
174
|
+
}
|
|
90
175
|
|
|
91
176
|
/**
|
|
92
|
-
* Like the default Schema `ReadonlySet` but with `withDefault` => new Set()
|
|
177
|
+
* Like the default Schema `ReadonlySet` but from Array with `withDefault` => new Set()
|
|
93
178
|
*/
|
|
94
|
-
export const ReadonlySet = <
|
|
179
|
+
export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
|
|
95
180
|
pipe(
|
|
96
|
-
|
|
97
|
-
(s) =>
|
|
181
|
+
ReadonlySetFromArray(value),
|
|
182
|
+
(s) =>
|
|
183
|
+
Object.assign(s, {
|
|
184
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))),
|
|
185
|
+
withDecodingDefaultType: s.pipe(
|
|
186
|
+
S.withDecodingDefaultType(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))
|
|
187
|
+
)
|
|
188
|
+
})
|
|
98
189
|
)
|
|
99
190
|
|
|
100
191
|
/**
|
|
101
|
-
* Like the default Schema `ReadonlyMap` but with `withDefault` => new Map()
|
|
192
|
+
* Like the default Schema `ReadonlyMap` but from Array with `withDefault` => new Map()
|
|
102
193
|
*/
|
|
103
|
-
export const ReadonlyMap = <
|
|
104
|
-
readonly key:
|
|
105
|
-
readonly value:
|
|
194
|
+
export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
195
|
+
readonly key: KeySchema
|
|
196
|
+
readonly value: ValueSchema
|
|
106
197
|
}) =>
|
|
107
198
|
pipe(
|
|
108
|
-
|
|
109
|
-
(s) =>
|
|
199
|
+
ReadonlyMapFromArray(pair),
|
|
200
|
+
(s) =>
|
|
201
|
+
Object.assign(s, {
|
|
202
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
|
|
203
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
|
|
204
|
+
})
|
|
110
205
|
)
|
|
111
206
|
|
|
112
207
|
/**
|
|
113
208
|
* Like the default Schema `NullOr` but with `withDefault` => null
|
|
114
209
|
*/
|
|
115
|
-
export const NullOr = <
|
|
210
|
+
export const NullOr = <Schema extends S.Top>(self: Schema) =>
|
|
116
211
|
pipe(
|
|
117
212
|
S.NullOr(self),
|
|
118
|
-
(s) =>
|
|
213
|
+
(s) =>
|
|
214
|
+
Object.assign(s, {
|
|
215
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
|
|
216
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
|
|
217
|
+
})
|
|
119
218
|
)
|
|
120
219
|
|
|
121
|
-
export const defaultDate =
|
|
220
|
+
export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
221
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
|
|
122
222
|
|
|
123
|
-
export const defaultBool =
|
|
223
|
+
export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
224
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
|
|
124
225
|
|
|
125
|
-
export const defaultNullable = (
|
|
126
|
-
|
|
127
|
-
) => s.pipe(withDefaultConstructor(() => null))
|
|
226
|
+
export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
227
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
|
|
128
228
|
|
|
129
|
-
export const defaultArray =
|
|
229
|
+
export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
230
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
|
|
130
231
|
|
|
131
|
-
export const defaultMap =
|
|
232
|
+
export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
233
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
|
|
132
234
|
|
|
133
|
-
export const defaultSet =
|
|
235
|
+
export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
236
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
|
|
134
237
|
|
|
135
238
|
export const withDefaultMake = <Self extends S.Top>(s: Self) => {
|
|
136
239
|
const a = Object.assign(S.decodeSync(s as any) as WithDefaults<Self>, s)
|
|
@@ -160,8 +263,11 @@ export type WithDefaults<Self extends S.Top> = (
|
|
|
160
263
|
// : never
|
|
161
264
|
|
|
162
265
|
export const inputDate = extendM(
|
|
163
|
-
S.Union([S.DateValid,
|
|
164
|
-
(s) => ({
|
|
266
|
+
S.Union([S.DateValid, Date]),
|
|
267
|
+
(s) => ({
|
|
268
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
|
|
269
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
|
|
270
|
+
})
|
|
165
271
|
)
|
|
166
272
|
|
|
167
273
|
export interface UnionBrand {}
|
|
@@ -211,7 +317,7 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
|
|
|
211
317
|
{ message: "One way schema transformation, encoding is not allowed" }
|
|
212
318
|
)
|
|
213
319
|
)
|
|
214
|
-
})
|
|
320
|
+
})
|
|
215
321
|
)
|
|
216
322
|
)
|
|
217
323
|
|
|
@@ -228,7 +334,7 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
228
334
|
S.decodeTo(
|
|
229
335
|
to,
|
|
230
336
|
SchemaTransformation.transformOrFail({
|
|
231
|
-
decode
|
|
337
|
+
decode,
|
|
232
338
|
encode: (i: any) =>
|
|
233
339
|
Effect.fail(
|
|
234
340
|
new SchemaIssue.Forbidden(
|
|
@@ -236,33 +342,36 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
236
342
|
{ message: "One way schema transformation, encoding is not allowed" }
|
|
237
343
|
)
|
|
238
344
|
)
|
|
239
|
-
})
|
|
345
|
+
})
|
|
240
346
|
)
|
|
241
347
|
)
|
|
242
348
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
context: ServiceMap.ServiceMap<R>
|
|
248
|
-
): any => {
|
|
349
|
+
export const provide: {
|
|
350
|
+
<R>(context: Context.Context<R>): <Self extends S.Top>(self: Self) => ProvidedCodec<Self, R>
|
|
351
|
+
<Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R>
|
|
352
|
+
} = Function.dual(2, <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R> => {
|
|
249
353
|
const prov = Effect.provide(context)
|
|
250
|
-
return
|
|
251
|
-
.
|
|
252
|
-
.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
354
|
+
return self.pipe(
|
|
355
|
+
S.middlewareDecoding((effect) => prov(effect)),
|
|
356
|
+
S.middlewareEncoding((effect) => prov(effect))
|
|
357
|
+
)
|
|
358
|
+
})
|
|
359
|
+
export const contextFromServices = Effect.fnUntraced(function*<
|
|
360
|
+
Self extends S.Top,
|
|
361
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
362
|
+
>(self: Self, ...services: Tags) {
|
|
363
|
+
const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
|
|
364
|
+
yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
|
|
365
|
+
)
|
|
366
|
+
return provide(self, context)
|
|
367
|
+
}) as <
|
|
368
|
+
Self extends S.Top,
|
|
369
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
370
|
+
>(
|
|
371
|
+
self: Self,
|
|
372
|
+
...services: Tags
|
|
373
|
+
) => Effect.Effect<
|
|
374
|
+
ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
|
|
375
|
+
never,
|
|
376
|
+
Context.Service.Identifier<Tags[number]>
|
|
377
|
+
>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pipe } from "effect"
|
|
1
|
+
import { Effect, pipe } from "effect"
|
|
2
2
|
import type { Refinement } from "effect-app/Function"
|
|
3
3
|
import { extendM } from "effect-app/utils"
|
|
4
4
|
import * as S from "effect/Schema"
|
|
@@ -6,7 +6,7 @@ import type { Simplify } from "effect/Types"
|
|
|
6
6
|
import { customRandom, nanoid, urlAlphabet } from "nanoid"
|
|
7
7
|
import validator from "validator"
|
|
8
8
|
import { fromBrand, nominal } from "./brand.js"
|
|
9
|
-
import {
|
|
9
|
+
import { withDefaultMake, type WithDefaults } from "./ext.js"
|
|
10
10
|
import { type B } from "./schema.js"
|
|
11
11
|
import type { NonEmptyString255Brand, NonEmptyStringBrand } from "./strings.js"
|
|
12
12
|
|
|
@@ -27,7 +27,7 @@ export type NonEmptyString50 = string & NonEmptyString50Brand
|
|
|
27
27
|
*/
|
|
28
28
|
export const NonEmptyString50 = nonEmptyString.pipe(
|
|
29
29
|
S.check(S.isMaxLength(50)),
|
|
30
|
-
fromBrand(nominal<NonEmptyString50>(), {
|
|
30
|
+
fromBrand<NonEmptyString50>(nominal<NonEmptyString50>(), {
|
|
31
31
|
identifier: "NonEmptyString50",
|
|
32
32
|
title: "NonEmptyString50",
|
|
33
33
|
jsonSchema: {}
|
|
@@ -50,7 +50,7 @@ export type NonEmptyString64 = string & NonEmptyString64Brand
|
|
|
50
50
|
*/
|
|
51
51
|
export const NonEmptyString64 = nonEmptyString.pipe(
|
|
52
52
|
S.check(S.isMaxLength(64)),
|
|
53
|
-
fromBrand(nominal<NonEmptyString64>(), {
|
|
53
|
+
fromBrand<NonEmptyString64>(nominal<NonEmptyString64>(), {
|
|
54
54
|
identifier: "NonEmptyString64",
|
|
55
55
|
title: "NonEmptyString64",
|
|
56
56
|
jsonSchema: {}
|
|
@@ -74,7 +74,7 @@ export type NonEmptyString80 = string & NonEmptyString80Brand
|
|
|
74
74
|
|
|
75
75
|
export const NonEmptyString80 = nonEmptyString.pipe(
|
|
76
76
|
S.check(S.isMaxLength(80)),
|
|
77
|
-
fromBrand(nominal<NonEmptyString80>(), {
|
|
77
|
+
fromBrand<NonEmptyString80>(nominal<NonEmptyString80>(), {
|
|
78
78
|
identifier: "NonEmptyString80",
|
|
79
79
|
title: "NonEmptyString80",
|
|
80
80
|
jsonSchema: {}
|
|
@@ -97,7 +97,7 @@ export type NonEmptyString100 = string & NonEmptyString100Brand
|
|
|
97
97
|
*/
|
|
98
98
|
export const NonEmptyString100 = nonEmptyString.pipe(
|
|
99
99
|
S.check(S.isMaxLength(100)),
|
|
100
|
-
fromBrand(nominal<NonEmptyString100>(), {
|
|
100
|
+
fromBrand<NonEmptyString100>(nominal<NonEmptyString100>(), {
|
|
101
101
|
identifier: "NonEmptyString100",
|
|
102
102
|
title: "NonEmptyString100",
|
|
103
103
|
jsonSchema: {}
|
|
@@ -121,7 +121,11 @@ export type Min3String255 = string & Min3String255Brand
|
|
|
121
121
|
export const Min3String255 = pipe(
|
|
122
122
|
S.String,
|
|
123
123
|
S.check(S.isMinLength(3), S.isMaxLength(255)),
|
|
124
|
-
fromBrand(nominal<Min3String255>(), {
|
|
124
|
+
fromBrand<Min3String255>(nominal<Min3String255>(), {
|
|
125
|
+
identifier: "Min3String255",
|
|
126
|
+
title: "Min3String255",
|
|
127
|
+
jsonSchema: {}
|
|
128
|
+
}),
|
|
125
129
|
withDefaultMake
|
|
126
130
|
)
|
|
127
131
|
|
|
@@ -140,10 +144,10 @@ const minLength = 6
|
|
|
140
144
|
const maxLength = 50
|
|
141
145
|
const size = 21
|
|
142
146
|
const length = 10 * size
|
|
143
|
-
const StringIdArb = (): S.LazyArbitrary<
|
|
147
|
+
const StringIdArb = (): S.LazyArbitrary<StringId> => (fc) =>
|
|
144
148
|
fc
|
|
145
149
|
.uint8Array({ minLength: length, maxLength: length })
|
|
146
|
-
.map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))())
|
|
150
|
+
.map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))() as StringId)
|
|
147
151
|
/**
|
|
148
152
|
* A string that is at least 6 characters long and a maximum of 50.
|
|
149
153
|
*/
|
|
@@ -151,16 +155,16 @@ export const StringId = extendM(
|
|
|
151
155
|
pipe(
|
|
152
156
|
S.String,
|
|
153
157
|
S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)),
|
|
154
|
-
fromBrand(nominal<StringId>(), {
|
|
158
|
+
fromBrand<StringId>(nominal<StringId>(), {
|
|
155
159
|
identifier: "StringId",
|
|
156
160
|
title: "StringId",
|
|
157
|
-
|
|
161
|
+
toArbitrary: () => (fc) => StringIdArb()(fc),
|
|
158
162
|
jsonSchema: {}
|
|
159
163
|
})
|
|
160
164
|
),
|
|
161
165
|
(s) => ({
|
|
162
166
|
make: makeStringId,
|
|
163
|
-
withDefault: s.pipe(
|
|
167
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(makeStringId)))
|
|
164
168
|
})
|
|
165
169
|
)
|
|
166
170
|
.pipe(withDefaultMake)
|
|
@@ -182,14 +186,16 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
182
186
|
(x) => (pref + x.substring(0, 50 - pref.length)) as Brand
|
|
183
187
|
)
|
|
184
188
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
185
|
-
const s
|
|
189
|
+
const s = StringId
|
|
186
190
|
.pipe(
|
|
187
191
|
S.refine((x: string): x is string & Brand => x.startsWith(pref), {
|
|
188
|
-
arbitrary: arb,
|
|
189
192
|
identifier: name,
|
|
190
193
|
title: name
|
|
194
|
+
}),
|
|
195
|
+
S.annotate({
|
|
196
|
+
toArbitrary: () => (fc) => arb()(fc)
|
|
191
197
|
})
|
|
192
|
-
)
|
|
198
|
+
)
|
|
193
199
|
const schema = s.pipe(withDefaultMake)
|
|
194
200
|
const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Brand
|
|
195
201
|
|
|
@@ -206,7 +212,9 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
206
212
|
*/
|
|
207
213
|
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => ex(str),
|
|
208
214
|
prefix,
|
|
209
|
-
withDefault: schema.pipe(
|
|
215
|
+
withDefault: schema.pipe(S.withConstructorDefault<S.Codec<Brand, string> & S.WithoutConstructorDefault>(
|
|
216
|
+
Effect.sync(make)
|
|
217
|
+
))
|
|
210
218
|
})
|
|
211
219
|
)
|
|
212
220
|
}
|
|
@@ -245,11 +253,17 @@ const isUrl: Refinement<string, Url> = (s: string): s is Url => {
|
|
|
245
253
|
export const Url = S
|
|
246
254
|
.String
|
|
247
255
|
.pipe(
|
|
256
|
+
S.annotate({
|
|
257
|
+
title: "Url",
|
|
258
|
+
format: "uri"
|
|
259
|
+
}),
|
|
248
260
|
S.refine(isUrl, {
|
|
249
|
-
arbitrary: (): S.LazyArbitrary<Url> => (fc) => fc.webUrl().map((_) => _ as Url),
|
|
250
261
|
identifier: "Url",
|
|
251
262
|
title: "Url",
|
|
252
263
|
jsonSchema: { format: "uri" }
|
|
253
264
|
}),
|
|
265
|
+
S.annotate({
|
|
266
|
+
toArbitrary: () => (fc) => fc.webUrl().map((_) => _ as Url)
|
|
267
|
+
}),
|
|
254
268
|
withDefaultMake
|
|
255
269
|
)
|
package/src/Schema/numbers.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
1
2
|
import { extendM } from "effect-app/utils"
|
|
2
3
|
import * as S from "effect/Schema"
|
|
3
4
|
import type { Simplify } from "effect/Types"
|
|
4
5
|
import { fromBrand, nominal } from "./brand.js"
|
|
5
|
-
import {
|
|
6
|
+
import { withDefaultMake } from "./ext.js"
|
|
6
7
|
import { type B } from "./schema.js"
|
|
7
8
|
|
|
8
9
|
export interface PositiveIntBrand
|
|
@@ -11,10 +12,10 @@ export interface PositiveIntBrand
|
|
|
11
12
|
export const PositiveInt = extendM(
|
|
12
13
|
S.Int.pipe(
|
|
13
14
|
S.check(S.isGreaterThan(0)),
|
|
14
|
-
fromBrand(nominal<PositiveInt>(), { identifier: "PositiveInt", title: "PositiveInt", jsonSchema: {} }),
|
|
15
|
+
fromBrand<PositiveInt>(nominal<PositiveInt>(), { identifier: "PositiveInt", title: "PositiveInt", jsonSchema: {} }),
|
|
15
16
|
withDefaultMake
|
|
16
17
|
),
|
|
17
|
-
(s) => ({ withDefault: s.pipe(
|
|
18
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) })
|
|
18
19
|
)
|
|
19
20
|
export type PositiveInt = number & PositiveIntBrand
|
|
20
21
|
|
|
@@ -22,53 +23,53 @@ export interface NonNegativeIntBrand extends Simplify<B.Brand<"NonNegativeInt">
|
|
|
22
23
|
export const NonNegativeInt = extendM(
|
|
23
24
|
S.Int.pipe(
|
|
24
25
|
S.check(S.isGreaterThanOrEqualTo(0)),
|
|
25
|
-
fromBrand(nominal<NonNegativeInt>(), {
|
|
26
|
+
fromBrand<NonNegativeInt>(nominal<NonNegativeInt>(), {
|
|
26
27
|
identifier: "NonNegativeInt",
|
|
27
28
|
title: "NonNegativeInt",
|
|
28
29
|
jsonSchema: {}
|
|
29
30
|
}),
|
|
30
31
|
withDefaultMake
|
|
31
32
|
),
|
|
32
|
-
(s) => ({ withDefault: s.pipe(
|
|
33
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
|
|
33
34
|
)
|
|
34
35
|
export type NonNegativeInt = number & NonNegativeIntBrand
|
|
35
36
|
|
|
36
37
|
export interface IntBrand extends Simplify<B.Brand<"Int">> {}
|
|
37
38
|
export const Int = extendM(
|
|
38
|
-
S.Int.pipe(fromBrand(nominal<Int>(), { identifier: "Int", title: "Int", jsonSchema: {} }), withDefaultMake),
|
|
39
|
-
(s) => ({ withDefault: s.pipe(
|
|
39
|
+
S.Int.pipe(fromBrand<Int>(nominal<Int>(), { identifier: "Int", title: "Int", jsonSchema: {} }), withDefaultMake),
|
|
40
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
|
|
40
41
|
)
|
|
41
42
|
export type Int = number & IntBrand
|
|
42
43
|
|
|
43
44
|
export interface PositiveNumberBrand extends Simplify<B.Brand<"PositiveNumber"> & NonNegativeNumberBrand> {}
|
|
44
45
|
export const PositiveNumber = extendM(
|
|
45
|
-
S.
|
|
46
|
+
S.Finite.pipe(
|
|
46
47
|
S.check(S.isGreaterThan(0)),
|
|
47
|
-
fromBrand(nominal<PositiveNumber>(), {
|
|
48
|
+
fromBrand<PositiveNumber>(nominal<PositiveNumber>(), {
|
|
48
49
|
identifier: "PositiveNumber",
|
|
49
50
|
title: "PositiveNumber",
|
|
50
51
|
jsonSchema: {}
|
|
51
52
|
}),
|
|
52
53
|
withDefaultMake
|
|
53
54
|
),
|
|
54
|
-
(s) => ({ withDefault: s.pipe(
|
|
55
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) })
|
|
55
56
|
)
|
|
56
57
|
export type PositiveNumber = number & PositiveNumberBrand
|
|
57
58
|
|
|
58
59
|
export interface NonNegativeNumberBrand extends Simplify<B.Brand<"NonNegativeNumber">> {}
|
|
59
60
|
export const NonNegativeNumber = extendM(
|
|
60
61
|
S
|
|
61
|
-
.
|
|
62
|
+
.Finite
|
|
62
63
|
.pipe(
|
|
63
64
|
S.check(S.isGreaterThanOrEqualTo(0)),
|
|
64
|
-
fromBrand(nominal<NonNegativeNumber>(), {
|
|
65
|
+
fromBrand<NonNegativeNumber>(nominal<NonNegativeNumber>(), {
|
|
65
66
|
identifier: "NonNegativeNumber",
|
|
66
67
|
title: "NonNegativeNumber",
|
|
67
68
|
jsonSchema: {}
|
|
68
69
|
}),
|
|
69
70
|
withDefaultMake
|
|
70
71
|
),
|
|
71
|
-
(s) => ({ withDefault: s.pipe(
|
|
72
|
+
(s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
|
|
72
73
|
)
|
|
73
74
|
export type NonNegativeNumber = number & NonNegativeNumberBrand
|
|
74
75
|
|
|
@@ -13,12 +13,19 @@ export type PhoneNumber = string & PhoneNumberBrand
|
|
|
13
13
|
export const PhoneNumber = S
|
|
14
14
|
.String
|
|
15
15
|
.pipe(
|
|
16
|
+
S.annotate({
|
|
17
|
+
title: "PhoneNumber",
|
|
18
|
+
description: "a phone number with at least 7 digits",
|
|
19
|
+
format: "phone"
|
|
20
|
+
}),
|
|
16
21
|
S.refine(isValidPhone as Refinement<string, PhoneNumber>, {
|
|
17
22
|
identifier: "PhoneNumber",
|
|
18
23
|
title: "PhoneNumber",
|
|
19
24
|
description: "a phone number with at least 7 digits",
|
|
20
|
-
arbitrary: () => (fc: any) => Numbers(7, 10)(fc).map((_: any) => _ as PhoneNumber),
|
|
21
25
|
jsonSchema: { format: "phone" }
|
|
22
26
|
}),
|
|
27
|
+
S.annotate({
|
|
28
|
+
toArbitrary: () => (fc) => Numbers(7, 10)(fc).map((_) => _ as PhoneNumber)
|
|
29
|
+
}),
|
|
23
30
|
withDefaultMake
|
|
24
31
|
)
|