effect-app 4.0.0-beta.7 → 4.0.0-beta.71
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 +313 -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/Context.d.ts +40 -0
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +66 -0
- package/dist/Effect.d.ts +8 -7
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -2
- 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 +61 -25
- 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 +39 -1
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +89 -12
- package/dist/Schema/SpecialJsonSchema.d.ts +21 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +59 -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 +8 -5
- 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 +81 -45
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +94 -49
- package/dist/Schema/moreStrings.d.ts +6 -6
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +14 -9
- package/dist/Schema/numbers.d.ts +11 -11
- 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 +4 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema.d.ts +22 -55
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +43 -64
- package/dist/client/apiClientFactory.d.ts +3 -3
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +14 -15
- package/dist/client/errors.d.ts +17 -9
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +35 -10
- package/dist/client/makeClient.d.ts +13 -12
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +5 -2
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -5
- package/dist/ids.d.ts +3 -3
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +3 -2
- package/dist/index.d.ts +3 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -9
- 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 +7 -6
- 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 +18 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -5
- package/package.json +29 -17
- 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} +57 -64
- package/src/Effect.ts +11 -9
- package/src/Layer.ts +5 -4
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +114 -16
- package/src/Schema/SpecialJsonSchema.ts +69 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +13 -7
- package/src/Schema/email.ts +10 -2
- package/src/Schema/ext.ts +182 -80
- package/src/Schema/moreStrings.ts +20 -10
- package/src/Schema/numbers.ts +9 -8
- package/src/Schema/phoneNumber.ts +8 -1
- package/src/Schema.ts +79 -103
- package/src/client/apiClientFactory.ts +18 -18
- package/src/client/errors.ts +46 -12
- package/src/client/makeClient.ts +32 -12
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +3 -2
- package/src/index.ts +3 -11
- package/src/middleware.ts +2 -2
- package/src/rpc/MiddlewareMaker.ts +8 -7
- 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 +26 -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 +26 -5
- package/test/schema.test.ts +396 -3
- package/test/secretURL.test.ts +157 -0
- package/test/special.test.ts +732 -0
- package/test/utils.test.ts +1 -1
- package/tsconfig.base.json +0 -1
- package/tsconfig.json +0 -1
- package/dist/ServiceMap.d.ts +0 -44
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/dist/Struct.d.ts +0 -44
- package/dist/Struct.d.ts.map +0 -1
- package/dist/Struct.js +0 -29
- package/src/Struct.ts +0 -54
package/src/Schema/ext.ts
CHANGED
|
@@ -1,51 +1,84 @@
|
|
|
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, 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
|
+
})
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Like the default Schema `DateValid` but from String with `withDefault` => now
|
|
56
|
+
*/
|
|
57
|
+
export const DateValid = Object.assign(Date.check(isDateValid()), {
|
|
58
|
+
withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
|
|
36
59
|
})
|
|
37
60
|
|
|
38
61
|
/**
|
|
39
62
|
* Like the default Schema `Boolean` but with `withDefault` => false
|
|
40
63
|
*/
|
|
41
64
|
export const Boolean = Object.assign(S.Boolean, {
|
|
42
|
-
withDefault: S.Boolean.pipe(
|
|
65
|
+
withDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false)))
|
|
43
66
|
})
|
|
44
67
|
|
|
45
68
|
/**
|
|
69
|
+
* You probably want to use `Finite` instead of this.
|
|
46
70
|
* Like the default Schema `Number` but with `withDefault` => 0
|
|
47
71
|
*/
|
|
48
|
-
export const Number = Object.assign(S.Number, {
|
|
72
|
+
export const Number = Object.assign(S.Number, {
|
|
73
|
+
withDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0)))
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Like the default Schema `Finite` but with `withDefault` => 0
|
|
78
|
+
*/
|
|
79
|
+
export const Finite = Object.assign(S.Finite, {
|
|
80
|
+
withDefault: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0)))
|
|
81
|
+
})
|
|
49
82
|
|
|
50
83
|
/**
|
|
51
84
|
* Like the default Schema `Literal` but with `withDefault` => literals[0]
|
|
@@ -58,79 +91,143 @@ export const Literal = <Literals extends NonEmptyReadonlyArray<AST.LiteralValue>
|
|
|
58
91
|
changeDefault: <A extends Literals[number]>(a: A) => {
|
|
59
92
|
return Object.assign(S.Literals(literals), {
|
|
60
93
|
Default: a,
|
|
61
|
-
withDefault: s.pipe(
|
|
94
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a)))
|
|
62
95
|
}) // todo: copy annotations from original?
|
|
63
96
|
},
|
|
64
97
|
Default: literals[0] as typeof literals[0],
|
|
65
|
-
withDefault: s.pipe(
|
|
98
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0])))
|
|
66
99
|
})
|
|
67
100
|
)
|
|
68
101
|
|
|
69
102
|
/**
|
|
70
103
|
* Like the default Schema `Array` but with `withDefault` => []
|
|
71
104
|
*/
|
|
72
|
-
export function Array<
|
|
105
|
+
export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
73
106
|
return pipe(
|
|
74
107
|
S.Array(value),
|
|
75
|
-
(s) => Object.assign(s, { withDefault: s.pipe(
|
|
108
|
+
(s) => Object.assign(s, { withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))) })
|
|
76
109
|
)
|
|
77
110
|
}
|
|
78
111
|
|
|
79
112
|
/**
|
|
80
|
-
*
|
|
113
|
+
* An annotated `S.Array` of unique items that decodes to a `ReadonlySet`.
|
|
81
114
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
115
|
+
export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema): S.Codec<
|
|
116
|
+
ReadonlySet<ValueSchema["Type"]>,
|
|
117
|
+
readonly ValueSchema["Encoded"][],
|
|
118
|
+
ValueSchema["DecodingServices"],
|
|
119
|
+
ValueSchema["EncodingServices"]
|
|
120
|
+
> => {
|
|
121
|
+
const from = S
|
|
122
|
+
.Array(value)
|
|
123
|
+
.annotate({ expected: "an array of unique items that will be decoded as a ReadonlySet" })
|
|
124
|
+
const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<S.Schema.Type<ValueSchema>>>
|
|
125
|
+
const schema = from.pipe(
|
|
126
|
+
S.decodeTo(
|
|
127
|
+
to,
|
|
128
|
+
SchemaTransformation.transform({
|
|
129
|
+
decode: (arr) => new Set(arr) as ReadonlySet<S.Schema.Type<ValueSchema>>,
|
|
130
|
+
encode: (set) => [...set]
|
|
131
|
+
})
|
|
132
|
+
)
|
|
86
133
|
)
|
|
134
|
+
return S.revealCodec(schema)
|
|
87
135
|
}
|
|
88
136
|
|
|
89
|
-
|
|
137
|
+
/**
|
|
138
|
+
* An annotated `S.Array` of key-value tuples that decodes to a `ReadonlyMap`.
|
|
139
|
+
*/
|
|
140
|
+
export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
141
|
+
readonly key: KeySchema
|
|
142
|
+
readonly value: ValueSchema
|
|
143
|
+
}): S.Codec<
|
|
144
|
+
ReadonlyMap<KeySchema["Type"], S.Schema.Type<ValueSchema>>,
|
|
145
|
+
readonly (readonly [KeySchema["Encoded"], ValueSchema["Encoded"]])[],
|
|
146
|
+
KeySchema["DecodingServices"] | ValueSchema["DecodingServices"],
|
|
147
|
+
KeySchema["EncodingServices"] | ValueSchema["EncodingServices"]
|
|
148
|
+
> => {
|
|
149
|
+
const from = S
|
|
150
|
+
.Array(S.Tuple([pair.key, pair.value]))
|
|
151
|
+
.annotate({ expected: "an array of key-value tuples that will be decoded as a ReadonlyMap" })
|
|
152
|
+
const to = S.instanceOf(Map) as S.instanceOf<
|
|
153
|
+
ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>
|
|
154
|
+
>
|
|
155
|
+
const schema = from.pipe(
|
|
156
|
+
S.decodeTo(
|
|
157
|
+
to,
|
|
158
|
+
SchemaTransformation.transform({
|
|
159
|
+
decode: (
|
|
160
|
+
arr
|
|
161
|
+
) => new Map(arr) as ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>,
|
|
162
|
+
encode: (
|
|
163
|
+
map
|
|
164
|
+
) => [...map.entries()] as any // fu
|
|
165
|
+
})
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
return S.revealCodec(schema)
|
|
169
|
+
}
|
|
90
170
|
|
|
91
171
|
/**
|
|
92
|
-
* Like the default Schema `ReadonlySet` but with `withDefault` => new Set()
|
|
172
|
+
* Like the default Schema `ReadonlySet` but from Array with `withDefault` => new Set()
|
|
93
173
|
*/
|
|
94
|
-
export const ReadonlySet = <
|
|
174
|
+
export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
|
|
95
175
|
pipe(
|
|
96
|
-
|
|
97
|
-
(s) =>
|
|
176
|
+
ReadonlySetFromArray(value),
|
|
177
|
+
(s) =>
|
|
178
|
+
Object.assign(s, {
|
|
179
|
+
withDefault: S.withConstructorDefault(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))(
|
|
180
|
+
s as typeof s & S.WithoutConstructorDefault
|
|
181
|
+
)
|
|
182
|
+
})
|
|
98
183
|
)
|
|
99
184
|
|
|
100
185
|
/**
|
|
101
|
-
* Like the default Schema `ReadonlyMap` but with `withDefault` => new Map()
|
|
186
|
+
* Like the default Schema `ReadonlyMap` but from Array with `withDefault` => new Map()
|
|
102
187
|
*/
|
|
103
|
-
export const ReadonlyMap = <
|
|
104
|
-
readonly key:
|
|
105
|
-
readonly value:
|
|
188
|
+
export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
189
|
+
readonly key: KeySchema
|
|
190
|
+
readonly value: ValueSchema
|
|
106
191
|
}) =>
|
|
107
192
|
pipe(
|
|
108
|
-
|
|
109
|
-
(s) =>
|
|
193
|
+
ReadonlyMapFromArray(pair),
|
|
194
|
+
(s) =>
|
|
195
|
+
Object.assign(s, {
|
|
196
|
+
withDefault: S.withConstructorDefault(Effect.sync(() => new Map()))(
|
|
197
|
+
s as typeof s & S.WithoutConstructorDefault
|
|
198
|
+
)
|
|
199
|
+
})
|
|
110
200
|
)
|
|
111
201
|
|
|
112
202
|
/**
|
|
113
203
|
* Like the default Schema `NullOr` but with `withDefault` => null
|
|
114
204
|
*/
|
|
115
|
-
export const NullOr = <
|
|
205
|
+
export const NullOr = <Schema extends S.Top>(self: Schema) =>
|
|
116
206
|
pipe(
|
|
117
207
|
S.NullOr(self),
|
|
118
|
-
(s) =>
|
|
208
|
+
(s) =>
|
|
209
|
+
Object.assign(s, {
|
|
210
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null)))
|
|
211
|
+
})
|
|
119
212
|
)
|
|
120
213
|
|
|
121
|
-
export const defaultDate =
|
|
214
|
+
export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
215
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
|
|
122
216
|
|
|
123
|
-
export const defaultBool =
|
|
217
|
+
export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
218
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
|
|
124
219
|
|
|
125
|
-
export const defaultNullable = (
|
|
126
|
-
|
|
127
|
-
) => s.pipe(withDefaultConstructor(() => null))
|
|
220
|
+
export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
221
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
|
|
128
222
|
|
|
129
|
-
export const defaultArray =
|
|
223
|
+
export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
224
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
|
|
130
225
|
|
|
131
|
-
export const defaultMap =
|
|
226
|
+
export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
227
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
|
|
132
228
|
|
|
133
|
-
export const defaultSet =
|
|
229
|
+
export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
230
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
|
|
134
231
|
|
|
135
232
|
export const withDefaultMake = <Self extends S.Top>(s: Self) => {
|
|
136
233
|
const a = Object.assign(S.decodeSync(s as any) as WithDefaults<Self>, s)
|
|
@@ -160,8 +257,12 @@ export type WithDefaults<Self extends S.Top> = (
|
|
|
160
257
|
// : never
|
|
161
258
|
|
|
162
259
|
export const inputDate = extendM(
|
|
163
|
-
S.Union([S.DateValid,
|
|
164
|
-
(s) => ({
|
|
260
|
+
S.Union([S.DateValid, Date]).pipe(S.revealCodec),
|
|
261
|
+
(s) => ({
|
|
262
|
+
withDefault: S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))(
|
|
263
|
+
s as typeof s & S.WithoutConstructorDefault
|
|
264
|
+
)
|
|
265
|
+
})
|
|
165
266
|
)
|
|
166
267
|
|
|
167
268
|
export interface UnionBrand {}
|
|
@@ -211,7 +312,7 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
|
|
|
211
312
|
{ message: "One way schema transformation, encoding is not allowed" }
|
|
212
313
|
)
|
|
213
314
|
)
|
|
214
|
-
})
|
|
315
|
+
})
|
|
215
316
|
)
|
|
216
317
|
)
|
|
217
318
|
|
|
@@ -228,7 +329,7 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
228
329
|
S.decodeTo(
|
|
229
330
|
to,
|
|
230
331
|
SchemaTransformation.transformOrFail({
|
|
231
|
-
decode
|
|
332
|
+
decode,
|
|
232
333
|
encode: (i: any) =>
|
|
233
334
|
Effect.fail(
|
|
234
335
|
new SchemaIssue.Forbidden(
|
|
@@ -236,33 +337,34 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
236
337
|
{ message: "One way schema transformation, encoding is not allowed" }
|
|
237
338
|
)
|
|
238
339
|
)
|
|
239
|
-
})
|
|
340
|
+
})
|
|
240
341
|
)
|
|
241
342
|
)
|
|
242
343
|
|
|
243
|
-
// TODO: v4 migration — S.declare API changed (no [self] + decode/encode pattern)
|
|
244
|
-
// Need to find v4 equivalent for contextual schema wrapping
|
|
245
344
|
export const provide = <Self extends S.Top, R>(
|
|
246
345
|
self: Self,
|
|
247
|
-
context:
|
|
248
|
-
):
|
|
346
|
+
context: Context.Context<R>
|
|
347
|
+
): ProvidedCodec<Self, R> => {
|
|
249
348
|
const prov = Effect.provide(context)
|
|
250
|
-
return
|
|
251
|
-
.
|
|
252
|
-
.
|
|
253
|
-
|
|
254
|
-
self,
|
|
255
|
-
SchemaTransformation.transformOrFail({
|
|
256
|
-
decode: (n: any) => prov(SchemaParser.decodeUnknownEffect(self)(n)),
|
|
257
|
-
encode: (n: any) => prov(SchemaParser.encodeUnknownEffect(self)(n))
|
|
258
|
-
}) as any
|
|
259
|
-
) as any
|
|
260
|
-
)
|
|
261
|
-
}
|
|
262
|
-
// TODO: v4 migration — ServiceMap.pick and S.declare pattern removed
|
|
263
|
-
export const contextFromServices = <Self extends S.Top, Tags extends readonly any[]>(
|
|
264
|
-
_self: Self,
|
|
265
|
-
..._services: Tags
|
|
266
|
-
): any => {
|
|
267
|
-
throw new Error("contextFromServices: not yet migrated to v4")
|
|
349
|
+
return self.pipe(
|
|
350
|
+
S.middlewareDecoding((effect) => prov(effect)),
|
|
351
|
+
S.middlewareEncoding((effect) => prov(effect))
|
|
352
|
+
) as ProvidedCodec<Self, R>
|
|
268
353
|
}
|
|
354
|
+
export const contextFromServices = <
|
|
355
|
+
Self extends S.Top,
|
|
356
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
357
|
+
>(
|
|
358
|
+
self: Self,
|
|
359
|
+
...services: Tags
|
|
360
|
+
): Effect.Effect<
|
|
361
|
+
ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
|
|
362
|
+
never,
|
|
363
|
+
Context.Service.Identifier<Tags[number]>
|
|
364
|
+
> =>
|
|
365
|
+
Effect.gen(function*() {
|
|
366
|
+
const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
|
|
367
|
+
yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
|
|
368
|
+
)
|
|
369
|
+
return provide(self, context)
|
|
370
|
+
})
|
|
@@ -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
|
|
|
@@ -140,10 +140,10 @@ const minLength = 6
|
|
|
140
140
|
const maxLength = 50
|
|
141
141
|
const size = 21
|
|
142
142
|
const length = 10 * size
|
|
143
|
-
const StringIdArb = (): S.LazyArbitrary<
|
|
143
|
+
const StringIdArb = (): S.LazyArbitrary<StringId> => (fc) =>
|
|
144
144
|
fc
|
|
145
145
|
.uint8Array({ minLength: length, maxLength: length })
|
|
146
|
-
.map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))())
|
|
146
|
+
.map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))() as StringId)
|
|
147
147
|
/**
|
|
148
148
|
* A string that is at least 6 characters long and a maximum of 50.
|
|
149
149
|
*/
|
|
@@ -154,13 +154,13 @@ export const StringId = extendM(
|
|
|
154
154
|
fromBrand(nominal<StringId>(), {
|
|
155
155
|
identifier: "StringId",
|
|
156
156
|
title: "StringId",
|
|
157
|
-
|
|
157
|
+
toArbitrary: () => (fc) => StringIdArb()(fc),
|
|
158
158
|
jsonSchema: {}
|
|
159
159
|
})
|
|
160
160
|
),
|
|
161
161
|
(s) => ({
|
|
162
162
|
make: makeStringId,
|
|
163
|
-
withDefault: s.pipe(
|
|
163
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(makeStringId)))
|
|
164
164
|
})
|
|
165
165
|
)
|
|
166
166
|
.pipe(withDefaultMake)
|
|
@@ -185,11 +185,13 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
185
185
|
const s: S.Codec<string & Brand, string> = StringId
|
|
186
186
|
.pipe(
|
|
187
187
|
S.refine((x: string): x is string & Brand => x.startsWith(pref), {
|
|
188
|
-
arbitrary: arb,
|
|
189
188
|
identifier: name,
|
|
190
189
|
title: name
|
|
190
|
+
}),
|
|
191
|
+
S.annotate({
|
|
192
|
+
toArbitrary: () => (fc) => arb()(fc)
|
|
191
193
|
})
|
|
192
|
-
) as
|
|
194
|
+
) as S.Codec<string & Brand, string>
|
|
193
195
|
const schema = s.pipe(withDefaultMake)
|
|
194
196
|
const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Brand
|
|
195
197
|
|
|
@@ -206,7 +208,9 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
206
208
|
*/
|
|
207
209
|
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => ex(str),
|
|
208
210
|
prefix,
|
|
209
|
-
withDefault:
|
|
211
|
+
withDefault: S.withConstructorDefault<S.Codec<Brand, string> & S.WithoutConstructorDefault>(
|
|
212
|
+
Effect.sync(make)
|
|
213
|
+
)(schema as S.Codec<Brand, string> & S.WithoutConstructorDefault)
|
|
210
214
|
})
|
|
211
215
|
)
|
|
212
216
|
}
|
|
@@ -245,11 +249,17 @@ const isUrl: Refinement<string, Url> = (s: string): s is Url => {
|
|
|
245
249
|
export const Url = S
|
|
246
250
|
.String
|
|
247
251
|
.pipe(
|
|
252
|
+
S.annotate({
|
|
253
|
+
title: "Url",
|
|
254
|
+
format: "uri"
|
|
255
|
+
}),
|
|
248
256
|
S.refine(isUrl, {
|
|
249
|
-
arbitrary: (): S.LazyArbitrary<Url> => (fc) => fc.webUrl().map((_) => _ as Url),
|
|
250
257
|
identifier: "Url",
|
|
251
258
|
title: "Url",
|
|
252
259
|
jsonSchema: { format: "uri" }
|
|
253
260
|
}),
|
|
261
|
+
S.annotate({
|
|
262
|
+
toArbitrary: () => (fc) => fc.webUrl().map((_) => _ as Url)
|
|
263
|
+
}),
|
|
254
264
|
withDefaultMake
|
|
255
265
|
)
|
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
|
|
@@ -14,7 +15,7 @@ export const PositiveInt = extendM(
|
|
|
14
15
|
fromBrand(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
|
|
|
@@ -29,20 +30,20 @@ export const NonNegativeInt = extendM(
|
|
|
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
39
|
S.Int.pipe(fromBrand(nominal<Int>(), { identifier: "Int", title: "Int", jsonSchema: {} }), withDefaultMake),
|
|
39
|
-
(s) => ({ withDefault: s.pipe(
|
|
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
48
|
fromBrand(nominal<PositiveNumber>(), {
|
|
48
49
|
identifier: "PositiveNumber",
|
|
@@ -51,14 +52,14 @@ export const PositiveNumber = extendM(
|
|
|
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
65
|
fromBrand(nominal<NonNegativeNumber>(), {
|
|
@@ -68,7 +69,7 @@ export const NonNegativeNumber = extendM(
|
|
|
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
|
)
|