effect-app 4.0.0-beta.21 → 4.0.0-beta.211
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 +954 -0
- package/dist/Array.d.ts +1 -1
- package/dist/Chunk.d.ts +1 -1
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Config/SecretURL.d.ts +1 -1
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +2 -2
- package/dist/Config/internal/configSecretURL.d.ts +1 -1
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- 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 +67 -0
- package/dist/Effect.d.ts +9 -10
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -6
- package/dist/Function.d.ts +1 -1
- package/dist/Function.d.ts.map +1 -1
- package/dist/Inputify.type.d.ts +1 -1
- package/dist/Layer.d.ts +7 -6
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +1 -1
- package/dist/NonEmptySet.d.ts +1 -1
- package/dist/NonEmptySet.d.ts.map +1 -1
- package/dist/Option.d.ts +1 -1
- package/dist/Option.d.ts.map +1 -1
- package/dist/Pure.d.ts +5 -5
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +13 -13
- package/dist/Schema/Class.d.ts +66 -20
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +189 -22
- package/dist/Schema/FastCheck.d.ts +1 -1
- package/dist/Schema/FastCheck.d.ts.map +1 -1
- package/dist/Schema/Methods.d.ts +1 -1
- package/dist/Schema/SchemaParser.d.ts +5 -0
- package/dist/Schema/SchemaParser.d.ts.map +1 -0
- package/dist/Schema/SchemaParser.js +6 -0
- 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 +4 -2
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +1 -1
- package/dist/Schema/email.d.ts +1 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +7 -4
- package/dist/Schema/ext.d.ts +117 -45
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +131 -42
- package/dist/Schema/moreStrings.d.ts +37 -25
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +15 -16
- package/dist/Schema/numbers.d.ts +15 -15
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +10 -12
- package/dist/Schema/phoneNumber.d.ts +1 -1
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +6 -3
- package/dist/Schema/schema.d.ts +1 -1
- package/dist/Schema/strings.d.ts +5 -5
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -5
- package/dist/Schema.d.ts +147 -15
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +131 -16
- package/dist/Set.d.ts +1 -1
- package/dist/Set.d.ts.map +1 -1
- package/dist/TypeTest.d.ts +1 -1
- package/dist/Types.d.ts +1 -1
- package/dist/Widen.type.d.ts +1 -1
- package/dist/_ext/Array.d.ts +1 -1
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/date.d.ts +1 -1
- package/dist/_ext/misc.d.ts +1 -1
- package/dist/_ext/ord.ext.d.ts +1 -1
- package/dist/_ext/ord.ext.d.ts.map +1 -1
- package/dist/builtin.d.ts +1 -1
- package/dist/builtin.d.ts.map +1 -1
- package/dist/client/InvalidationKeys.d.ts +29 -0
- package/dist/client/InvalidationKeys.d.ts.map +1 -0
- package/dist/client/InvalidationKeys.js +33 -0
- package/dist/client/apiClientFactory.d.ts +20 -32
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +95 -32
- package/dist/client/clientFor.d.ts +51 -17
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +49 -25
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +43 -17
- package/dist/client/makeClient.d.ts +481 -33
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +66 -24
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/faker.d.ts +1 -1
- package/dist/faker.d.ts.map +1 -1
- package/dist/http/Request.d.ts +2 -2
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/internal/lib.d.ts +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/ids.d.ts +12 -12
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +3 -2
- package/dist/index.d.ts +5 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -8
- package/dist/logger.d.ts +1 -1
- package/dist/middleware.d.ts +14 -8
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +14 -8
- package/dist/rpc/Invalidation.d.ts +402 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +150 -0
- package/dist/rpc/MiddlewareMaker.d.ts +5 -4
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +57 -37
- package/dist/rpc/RpcContextMap.d.ts +3 -3
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +5 -4
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/rpc.d.ts +2 -2
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -2
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +3 -3
- package/dist/utils/effectify.d.ts +1 -1
- package/dist/utils/extend.d.ts +1 -1
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/gen.d.ts +2 -2
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logLevel.d.ts +2 -2
- package/dist/utils/logLevel.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +3 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +3 -3
- package/dist/utils.d.ts +31 -38
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +12 -25
- package/dist/validation/validators.d.ts +1 -1
- package/dist/validation/validators.d.ts.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/package.json +46 -24
- package/src/Config/SecretURL.ts +2 -1
- package/src/Config.ts +14 -0
- package/src/ConfigProvider.ts +48 -0
- package/src/{ServiceMap.ts → Context.ts} +52 -59
- package/src/Effect.ts +12 -14
- package/src/Layer.ts +6 -5
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +268 -62
- package/src/Schema/SchemaParser.ts +12 -0
- package/src/Schema/SpecialJsonSchema.ts +137 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +21 -1
- package/src/Schema/email.ts +7 -2
- package/src/Schema/ext.ts +204 -72
- package/src/Schema/moreStrings.ts +40 -37
- package/src/Schema/numbers.ts +14 -16
- package/src/Schema/phoneNumber.ts +5 -1
- package/src/Schema/strings.ts +4 -8
- package/src/Schema.ts +314 -20
- package/src/client/InvalidationKeys.ts +50 -0
- package/src/client/apiClientFactory.ts +223 -129
- package/src/client/clientFor.ts +95 -29
- package/src/client/errors.ts +52 -26
- package/src/client/makeClient.ts +572 -71
- package/src/client.ts +1 -0
- package/src/ids.ts +3 -2
- package/src/index.ts +5 -10
- package/src/middleware.ts +13 -9
- package/src/rpc/Invalidation.ts +226 -0
- package/src/rpc/MiddlewareMaker.ts +65 -60
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +5 -4
- package/src/rpc.ts +1 -1
- package/src/transform.ts +2 -2
- package/src/utils/gen.ts +1 -1
- package/src/utils/logger.ts +2 -2
- package/src/utils.ts +50 -132
- 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/dist/stream-error.types.d.ts +2 -0
- package/test/dist/stream-error.types.d.ts.map +1 -0
- package/test/dist/stream-error.types.js +27 -0
- package/test/rpc.test.ts +45 -6
- package/test/schema.test.ts +581 -7
- package/test/secretURL.test.ts +157 -0
- package/test/special.test.ts +1023 -0
- package/test/utils.test.ts +6 -6
- package/tsconfig.base.json +3 -4
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +29 -29
- package/tsconfig.test.json +2 -2
- package/dist/Operations.d.ts +0 -123
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -29
- package/dist/ServiceMap.d.ts +0 -44
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
package/src/Schema/email.ts
CHANGED
|
@@ -12,11 +12,16 @@ export type Email = string & EmailBrand
|
|
|
12
12
|
export const Email = S
|
|
13
13
|
.String
|
|
14
14
|
.pipe(
|
|
15
|
+
S.annotate({
|
|
16
|
+
title: "Email",
|
|
17
|
+
description: "an email according to RFC 5322",
|
|
18
|
+
format: "email"
|
|
19
|
+
}),
|
|
20
|
+
S.check(S.isMinLength(3), /* a@b */ S.isMaxLength(998)),
|
|
15
21
|
S.refine(isValidEmail as Refinement<string, Email>, {
|
|
16
22
|
identifier: "Email",
|
|
17
|
-
title: "Email",
|
|
18
23
|
description: "an email according to RFC 5322",
|
|
19
|
-
jsonSchema: { format: "email", minLength: 3,
|
|
24
|
+
jsonSchema: { format: "email", minLength: 3, maxLength: 998 }
|
|
20
25
|
}),
|
|
21
26
|
S.annotate({
|
|
22
27
|
toArbitrary: () => (fc) => fc.emailAddress().map((_) => _ as Email)
|
package/src/Schema/ext.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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 { Config, 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
|
|
|
@@ -13,51 +15,111 @@ type ProvidedCodec<Self extends S.Top, R> = S.Codec<
|
|
|
13
15
|
Exclude<Self["EncodingServices"], R>
|
|
14
16
|
>
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return S.withConstructorDefault<Narrowed>(
|
|
26
|
-
() => Option.some(makeDefault() as Narrowed["~type.make.in"])
|
|
27
|
-
)(self as Narrowed)
|
|
18
|
+
const concurrencySetting = Effect.runSync(
|
|
19
|
+
Config
|
|
20
|
+
.literal("unbounded", "SCHEMA_CONCURRENCY")
|
|
21
|
+
.pipe(Config.orElse(() => Config.number("SCHEMA_CONCURRENCY")), Config.option)
|
|
22
|
+
.asEffect()
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
export const DefaultParseOptions: SchemaAST.ParseOptions = {
|
|
26
|
+
concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const)
|
|
28
27
|
}
|
|
29
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Parse-options annotation used on schema constructors for decode paths where callers
|
|
31
|
+
* cannot currently pass parse options (notably some RPC / HttpApi integration paths).
|
|
32
|
+
*
|
|
33
|
+
* Keep this annotation in place so those framework-managed decodes still run with
|
|
34
|
+
* unbounded concurrency by default.
|
|
35
|
+
*/
|
|
36
|
+
export const concurrencyUnbounded = { parseOptions: DefaultParseOptions } as const
|
|
37
|
+
|
|
38
|
+
type DecodeLike = (schema: any) => (input: any, options?: SchemaAST.ParseOptions) => any
|
|
39
|
+
|
|
40
|
+
export const withDefaultParseOptions = <Decode extends DecodeLike>(
|
|
41
|
+
decode: Decode,
|
|
42
|
+
defaultParseOptions: SchemaAST.ParseOptions = DefaultParseOptions
|
|
43
|
+
): Decode =>
|
|
44
|
+
((schema: any) => {
|
|
45
|
+
const run = decode(schema)
|
|
46
|
+
return (input: any, options?: SchemaAST.ParseOptions) => run(input, { ...defaultParseOptions, ...options })
|
|
47
|
+
}) as Decode
|
|
48
|
+
|
|
30
49
|
// TODO: v4 migration - Date is no longer by default encoded to string.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
50
|
+
|
|
51
|
+
const DateString = S.String.annotate({
|
|
52
|
+
identifier: "Date",
|
|
53
|
+
description: "a string in ISO 8601 format that will be decoded as a Date",
|
|
54
|
+
format: "date-time"
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Schema type for {@link DateFromString}.
|
|
59
|
+
*
|
|
60
|
+
* @category Schemas
|
|
61
|
+
* @since 4.0.0
|
|
62
|
+
*/
|
|
63
|
+
export interface DateFromString extends S.decodeTo<S.Date, S.String> {}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A transformation schema that parses an ISO 8601 string into a `Date`.
|
|
67
|
+
*
|
|
68
|
+
* Decoding:
|
|
69
|
+
* - A `string` is decoded as a `Date`.
|
|
70
|
+
*
|
|
71
|
+
* Encoding:
|
|
72
|
+
* - A `Date` is encoded as a `string`.
|
|
73
|
+
*
|
|
74
|
+
* @since 4.0.0
|
|
75
|
+
*/
|
|
76
|
+
export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString))
|
|
37
77
|
|
|
38
78
|
/**
|
|
39
79
|
* Like the default Schema `Date` but from String with `withDefault` => now
|
|
40
80
|
*/
|
|
41
81
|
export const Date = Object.assign(DateFromString, {
|
|
42
|
-
withDefault: DateFromString.pipe(
|
|
82
|
+
withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
|
|
83
|
+
withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Like the default Schema `DateValid` but from String with `withDefault` => now
|
|
88
|
+
*/
|
|
89
|
+
export const DateValid = Object.assign(Date.check(isDateValid()), {
|
|
90
|
+
withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
|
|
91
|
+
withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
|
|
43
92
|
})
|
|
44
93
|
|
|
45
94
|
/**
|
|
46
95
|
* Like the default Schema `Boolean` but with `withDefault` => false
|
|
47
96
|
*/
|
|
48
97
|
export const Boolean = Object.assign(S.Boolean, {
|
|
49
|
-
withDefault: S.Boolean.pipe(
|
|
98
|
+
withDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
|
|
99
|
+
withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
|
|
50
100
|
})
|
|
51
101
|
|
|
52
102
|
/**
|
|
103
|
+
* You probably want to use `Finite` instead of this.
|
|
53
104
|
* Like the default Schema `Number` but with `withDefault` => 0
|
|
54
105
|
*/
|
|
55
|
-
export const Number = Object.assign(S.Number, {
|
|
106
|
+
export const Number = Object.assign(S.Number, {
|
|
107
|
+
withDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
108
|
+
withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Like the default Schema `Finite` but with `withDefault` => 0
|
|
113
|
+
*/
|
|
114
|
+
export const Finite = Object.assign(S.Finite, {
|
|
115
|
+
withDefault: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
116
|
+
withDecodingDefaultType: S.Finite.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
117
|
+
})
|
|
56
118
|
|
|
57
119
|
/**
|
|
58
|
-
* Like the default Schema `
|
|
120
|
+
* Like the default Schema `Literals` but with `withDefault` => literals[0]
|
|
59
121
|
*/
|
|
60
|
-
export const
|
|
122
|
+
export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
|
|
61
123
|
pipe(
|
|
62
124
|
S.Literals(literals),
|
|
63
125
|
(s) =>
|
|
@@ -65,11 +127,14 @@ export const Literal = <Literals extends NonEmptyReadonlyArray<AST.LiteralValue>
|
|
|
65
127
|
changeDefault: <A extends Literals[number]>(a: A) => {
|
|
66
128
|
return Object.assign(S.Literals(literals), {
|
|
67
129
|
Default: a,
|
|
68
|
-
withDefault: s.pipe(
|
|
130
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
|
|
131
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
|
|
69
132
|
}) // todo: copy annotations from original?
|
|
70
133
|
},
|
|
71
|
-
|
|
72
|
-
|
|
134
|
+
// 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
|
|
135
|
+
Default: literals[0] as Literals[0],
|
|
136
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
|
|
137
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
|
|
73
138
|
})
|
|
74
139
|
)
|
|
75
140
|
|
|
@@ -78,43 +143,96 @@ export const Literal = <Literals extends NonEmptyReadonlyArray<AST.LiteralValue>
|
|
|
78
143
|
*/
|
|
79
144
|
export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
80
145
|
return pipe(
|
|
81
|
-
S.Array(value),
|
|
82
|
-
(s) =>
|
|
146
|
+
S.Array(value).annotate(concurrencyUnbounded),
|
|
147
|
+
(s) =>
|
|
148
|
+
Object.assign(s, {
|
|
149
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
|
|
150
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
|
|
151
|
+
})
|
|
83
152
|
)
|
|
84
153
|
}
|
|
85
154
|
|
|
86
155
|
/**
|
|
87
|
-
*
|
|
156
|
+
* An annotated `S.Array` of unique items that decodes to a `ReadonlySet`.
|
|
88
157
|
*/
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
(
|
|
158
|
+
export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
|
|
159
|
+
const from = S
|
|
160
|
+
.Array(value)
|
|
161
|
+
.annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" })
|
|
162
|
+
const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<S.Schema.Type<ValueSchema>>>
|
|
163
|
+
const schema = from.pipe(
|
|
164
|
+
S.decodeTo(
|
|
165
|
+
to,
|
|
166
|
+
SchemaTransformation.transform({
|
|
167
|
+
decode: (arr) => new Set(arr) as ReadonlySet<S.Schema.Type<ValueSchema>>,
|
|
168
|
+
encode: (set) => [...set]
|
|
169
|
+
})
|
|
170
|
+
)
|
|
93
171
|
)
|
|
172
|
+
return schema
|
|
94
173
|
}
|
|
95
174
|
|
|
96
|
-
|
|
175
|
+
/**
|
|
176
|
+
* An annotated `S.Array` of key-value tuples that decodes to a `ReadonlyMap`.
|
|
177
|
+
*/
|
|
178
|
+
export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
179
|
+
readonly key: KeySchema
|
|
180
|
+
readonly value: ValueSchema
|
|
181
|
+
}) => {
|
|
182
|
+
const from = S
|
|
183
|
+
.Array(S.Tuple([pair.key, pair.value]))
|
|
184
|
+
.annotate({
|
|
185
|
+
...concurrencyUnbounded,
|
|
186
|
+
expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
|
|
187
|
+
})
|
|
188
|
+
const to = S.instanceOf(Map) as S.instanceOf<
|
|
189
|
+
ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>
|
|
190
|
+
>
|
|
191
|
+
const schema = from.pipe(
|
|
192
|
+
S.decodeTo(
|
|
193
|
+
to,
|
|
194
|
+
SchemaTransformation.transform({
|
|
195
|
+
decode: (
|
|
196
|
+
arr
|
|
197
|
+
) => new Map(arr) as ReadonlyMap<S.Schema.Type<KeySchema>, S.Schema.Type<ValueSchema>>,
|
|
198
|
+
encode: (
|
|
199
|
+
map
|
|
200
|
+
) => [...map.entries()] as any // fu
|
|
201
|
+
})
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
return schema
|
|
205
|
+
}
|
|
97
206
|
|
|
98
207
|
/**
|
|
99
|
-
* Like the default Schema `ReadonlySet` but with `withDefault` => new Set()
|
|
208
|
+
* Like the default Schema `ReadonlySet` but from Array with `withDefault` => new Set()
|
|
100
209
|
*/
|
|
101
210
|
export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
|
|
102
211
|
pipe(
|
|
103
|
-
|
|
212
|
+
ReadonlySetFromArray(value),
|
|
104
213
|
(s) =>
|
|
105
|
-
Object.assign(s, {
|
|
214
|
+
Object.assign(s, {
|
|
215
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))),
|
|
216
|
+
withDecodingDefaultType: s.pipe(
|
|
217
|
+
S.withDecodingDefaultType(Effect.sync(() => new Set<S.Schema.Type<ValueSchema>>()))
|
|
218
|
+
)
|
|
219
|
+
})
|
|
106
220
|
)
|
|
107
221
|
|
|
108
222
|
/**
|
|
109
|
-
* Like the default Schema `ReadonlyMap` but with `withDefault` => new Map()
|
|
223
|
+
* Like the default Schema `ReadonlyMap` but from Array with `withDefault` => new Map()
|
|
110
224
|
*/
|
|
111
225
|
export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
112
226
|
readonly key: KeySchema
|
|
113
227
|
readonly value: ValueSchema
|
|
114
228
|
}) =>
|
|
115
229
|
pipe(
|
|
116
|
-
|
|
117
|
-
(s) =>
|
|
230
|
+
ReadonlyMapFromArray(pair),
|
|
231
|
+
(s) =>
|
|
232
|
+
Object.assign(s, {
|
|
233
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
|
|
234
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
|
|
235
|
+
})
|
|
118
236
|
)
|
|
119
237
|
|
|
120
238
|
/**
|
|
@@ -123,21 +241,30 @@ export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(
|
|
|
123
241
|
export const NullOr = <Schema extends S.Top>(self: Schema) =>
|
|
124
242
|
pipe(
|
|
125
243
|
S.NullOr(self),
|
|
126
|
-
(s) =>
|
|
244
|
+
(s) =>
|
|
245
|
+
Object.assign(s, {
|
|
246
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
|
|
247
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
|
|
248
|
+
})
|
|
127
249
|
)
|
|
128
250
|
|
|
129
|
-
export const defaultDate = <Schema extends S.Top>(schema: Schema) =>
|
|
130
|
-
schema.pipe(
|
|
251
|
+
export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
252
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
|
|
131
253
|
|
|
132
|
-
export const defaultBool = <Schema extends S.Top>(schema: Schema) =>
|
|
254
|
+
export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
255
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
|
|
133
256
|
|
|
134
|
-
export const defaultNullable = <Schema extends S.Top>(schema: Schema) =>
|
|
257
|
+
export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
258
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
|
|
135
259
|
|
|
136
|
-
export const defaultArray = <Schema extends S.Top>(schema: Schema) =>
|
|
260
|
+
export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
261
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
|
|
137
262
|
|
|
138
|
-
export const defaultMap = <Schema extends S.Top>(schema: Schema) =>
|
|
263
|
+
export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
264
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
|
|
139
265
|
|
|
140
|
-
export const defaultSet = <Schema extends S.Top>(schema: Schema) =>
|
|
266
|
+
export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
267
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
|
|
141
268
|
|
|
142
269
|
export const withDefaultMake = <Self extends S.Top>(s: Self) => {
|
|
143
270
|
const a = Object.assign(S.decodeSync(s as any) as WithDefaults<Self>, s)
|
|
@@ -167,8 +294,11 @@ export type WithDefaults<Self extends S.Top> = (
|
|
|
167
294
|
// : never
|
|
168
295
|
|
|
169
296
|
export const inputDate = extendM(
|
|
170
|
-
S.Union([S.DateValid,
|
|
171
|
-
(s) => ({
|
|
297
|
+
S.Union([S.DateValid, Date]),
|
|
298
|
+
(s) => ({
|
|
299
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
|
|
300
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
|
|
301
|
+
})
|
|
172
302
|
)
|
|
173
303
|
|
|
174
304
|
export interface UnionBrand {}
|
|
@@ -218,7 +348,7 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
|
|
|
218
348
|
{ message: "One way schema transformation, encoding is not allowed" }
|
|
219
349
|
)
|
|
220
350
|
)
|
|
221
|
-
})
|
|
351
|
+
})
|
|
222
352
|
)
|
|
223
353
|
)
|
|
224
354
|
|
|
@@ -235,7 +365,7 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
235
365
|
S.decodeTo(
|
|
236
366
|
to,
|
|
237
367
|
SchemaTransformation.transformOrFail({
|
|
238
|
-
decode
|
|
368
|
+
decode,
|
|
239
369
|
encode: (i: any) =>
|
|
240
370
|
Effect.fail(
|
|
241
371
|
new SchemaIssue.Forbidden(
|
|
@@ -243,34 +373,36 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
243
373
|
{ message: "One way schema transformation, encoding is not allowed" }
|
|
244
374
|
)
|
|
245
375
|
)
|
|
246
|
-
})
|
|
376
|
+
})
|
|
247
377
|
)
|
|
248
378
|
)
|
|
249
379
|
|
|
250
|
-
export const provide
|
|
251
|
-
self: Self,
|
|
252
|
-
context:
|
|
253
|
-
): ProvidedCodec<Self, R> => {
|
|
380
|
+
export const provide: {
|
|
381
|
+
<R>(context: Context.Context<R>): <Self extends S.Top>(self: Self) => ProvidedCodec<Self, R>
|
|
382
|
+
<Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R>
|
|
383
|
+
} = Function.dual(2, <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R> => {
|
|
254
384
|
const prov = Effect.provide(context)
|
|
255
385
|
return self.pipe(
|
|
256
386
|
S.middlewareDecoding((effect) => prov(effect)),
|
|
257
387
|
S.middlewareEncoding((effect) => prov(effect))
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
export const contextFromServices =
|
|
388
|
+
)
|
|
389
|
+
})
|
|
390
|
+
export const contextFromServices = Effect.fnUntraced(function*<
|
|
391
|
+
Self extends S.Top,
|
|
392
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
393
|
+
>(self: Self, ...services: Tags) {
|
|
394
|
+
const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
|
|
395
|
+
yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
|
|
396
|
+
)
|
|
397
|
+
return provide(self, context)
|
|
398
|
+
}) as <
|
|
261
399
|
Self extends S.Top,
|
|
262
|
-
Tags extends ReadonlyArray<
|
|
400
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
263
401
|
>(
|
|
264
402
|
self: Self,
|
|
265
403
|
...services: Tags
|
|
266
|
-
)
|
|
267
|
-
ProvidedCodec<Self,
|
|
404
|
+
) => Effect.Effect<
|
|
405
|
+
ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
|
|
268
406
|
never,
|
|
269
|
-
|
|
270
|
-
>
|
|
271
|
-
Effect.gen(function*() {
|
|
272
|
-
const context: ServiceMap.ServiceMap<ServiceMap.Service.Identifier<Tags[number]>> = ServiceMap.pick(...services)(
|
|
273
|
-
yield* Effect.services<ServiceMap.Service.Identifier<Tags[number]>>()
|
|
274
|
-
)
|
|
275
|
-
return provide(self, context)
|
|
276
|
-
})
|
|
407
|
+
Context.Service.Identifier<Tags[number]>
|
|
408
|
+
>
|
|
@@ -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,9 +27,8 @@ 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
|
-
title: "NonEmptyString50",
|
|
33
32
|
jsonSchema: {}
|
|
34
33
|
}),
|
|
35
34
|
withDefaultMake
|
|
@@ -50,9 +49,8 @@ export type NonEmptyString64 = string & NonEmptyString64Brand
|
|
|
50
49
|
*/
|
|
51
50
|
export const NonEmptyString64 = nonEmptyString.pipe(
|
|
52
51
|
S.check(S.isMaxLength(64)),
|
|
53
|
-
fromBrand(nominal<NonEmptyString64>(), {
|
|
52
|
+
fromBrand<NonEmptyString64>(nominal<NonEmptyString64>(), {
|
|
54
53
|
identifier: "NonEmptyString64",
|
|
55
|
-
title: "NonEmptyString64",
|
|
56
54
|
jsonSchema: {}
|
|
57
55
|
}),
|
|
58
56
|
withDefaultMake
|
|
@@ -74,9 +72,8 @@ export type NonEmptyString80 = string & NonEmptyString80Brand
|
|
|
74
72
|
|
|
75
73
|
export const NonEmptyString80 = nonEmptyString.pipe(
|
|
76
74
|
S.check(S.isMaxLength(80)),
|
|
77
|
-
fromBrand(nominal<NonEmptyString80>(), {
|
|
75
|
+
fromBrand<NonEmptyString80>(nominal<NonEmptyString80>(), {
|
|
78
76
|
identifier: "NonEmptyString80",
|
|
79
|
-
title: "NonEmptyString80",
|
|
80
77
|
jsonSchema: {}
|
|
81
78
|
}),
|
|
82
79
|
withDefaultMake
|
|
@@ -97,9 +94,8 @@ export type NonEmptyString100 = string & NonEmptyString100Brand
|
|
|
97
94
|
*/
|
|
98
95
|
export const NonEmptyString100 = nonEmptyString.pipe(
|
|
99
96
|
S.check(S.isMaxLength(100)),
|
|
100
|
-
fromBrand(nominal<NonEmptyString100>(), {
|
|
97
|
+
fromBrand<NonEmptyString100>(nominal<NonEmptyString100>(), {
|
|
101
98
|
identifier: "NonEmptyString100",
|
|
102
|
-
title: "NonEmptyString100",
|
|
103
99
|
jsonSchema: {}
|
|
104
100
|
}),
|
|
105
101
|
withDefaultMake
|
|
@@ -121,7 +117,10 @@ export type Min3String255 = string & Min3String255Brand
|
|
|
121
117
|
export const Min3String255 = pipe(
|
|
122
118
|
S.String,
|
|
123
119
|
S.check(S.isMinLength(3), S.isMaxLength(255)),
|
|
124
|
-
fromBrand(nominal<Min3String255>(), {
|
|
120
|
+
fromBrand<Min3String255>(nominal<Min3String255>(), {
|
|
121
|
+
identifier: "Min3String255",
|
|
122
|
+
jsonSchema: {}
|
|
123
|
+
}),
|
|
125
124
|
withDefaultMake
|
|
126
125
|
)
|
|
127
126
|
|
|
@@ -135,7 +134,8 @@ export interface StringIdBrand extends Simplify<B.Brand<"StringId"> & NonEmptySt
|
|
|
135
134
|
*/
|
|
136
135
|
export type StringId = string & StringIdBrand
|
|
137
136
|
|
|
138
|
-
const makeStringId = (): StringId =>
|
|
137
|
+
const makeStringId = (s?: string): StringId =>
|
|
138
|
+
s !== undefined ? S.decodeSync(StringId)(s) : nanoid() as unknown as StringId
|
|
139
139
|
const minLength = 6
|
|
140
140
|
const maxLength = 50
|
|
141
141
|
const size = 21
|
|
@@ -151,16 +151,15 @@ export const StringId = extendM(
|
|
|
151
151
|
pipe(
|
|
152
152
|
S.String,
|
|
153
153
|
S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)),
|
|
154
|
-
fromBrand(nominal<StringId>(), {
|
|
154
|
+
fromBrand<StringId>(nominal<StringId>(), {
|
|
155
155
|
identifier: "StringId",
|
|
156
|
-
title: "StringId",
|
|
157
156
|
toArbitrary: () => (fc) => StringIdArb()(fc),
|
|
158
157
|
jsonSchema: {}
|
|
159
158
|
})
|
|
160
159
|
),
|
|
161
160
|
(s) => ({
|
|
162
161
|
make: makeStringId,
|
|
163
|
-
withDefault: s.pipe(
|
|
162
|
+
withDefault: s.pipe(S.withConstructorDefault(Effect.sync(makeStringId)))
|
|
164
163
|
})
|
|
165
164
|
)
|
|
166
165
|
.pipe(withDefaultMake)
|
|
@@ -169,7 +168,7 @@ export const StringId = extendM(
|
|
|
169
168
|
|
|
170
169
|
// const prefixedStringIdUnsafeThunk = (prefix: string) => () => prefixedStringIdUnsafe(prefix)
|
|
171
170
|
|
|
172
|
-
export function prefixedStringId<
|
|
171
|
+
export function prefixedStringId<Type extends StringId>() {
|
|
173
172
|
return <Prefix extends string, Separator extends string = "-">(
|
|
174
173
|
prefix: Prefix,
|
|
175
174
|
name: string,
|
|
@@ -177,27 +176,26 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
177
176
|
) => {
|
|
178
177
|
type FullPrefix = `${Prefix}${Separator}`
|
|
179
178
|
const pref = `${prefix}${separator ?? "-"}` as FullPrefix
|
|
180
|
-
const arb = (): S.LazyArbitrary<
|
|
179
|
+
const arb = (): S.LazyArbitrary<Type> => (fc) =>
|
|
181
180
|
StringIdArb()(fc).map(
|
|
182
|
-
(x) => (pref + x.substring(0, 50 - pref.length)) as
|
|
181
|
+
(x) => (pref + x.substring(0, 50 - pref.length)) as Type
|
|
183
182
|
)
|
|
184
183
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
185
|
-
const s
|
|
184
|
+
const s = StringId
|
|
186
185
|
.pipe(
|
|
187
|
-
S.refine((x: string): x is
|
|
188
|
-
identifier: name
|
|
189
|
-
title: name
|
|
186
|
+
S.refine((x: string): x is Type => x.startsWith(pref), {
|
|
187
|
+
identifier: name
|
|
190
188
|
}),
|
|
191
189
|
S.annotate({
|
|
192
190
|
toArbitrary: () => (fc) => arb()(fc)
|
|
193
191
|
})
|
|
194
|
-
)
|
|
192
|
+
)
|
|
195
193
|
const schema = s.pipe(withDefaultMake)
|
|
196
|
-
const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as
|
|
194
|
+
const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Type
|
|
197
195
|
|
|
198
196
|
return extendM(
|
|
199
197
|
schema,
|
|
200
|
-
(ex): PrefixedStringUtils<
|
|
198
|
+
(ex): PrefixedStringUtils<Type, Prefix, Separator> => ({
|
|
201
199
|
make,
|
|
202
200
|
/**
|
|
203
201
|
* Automatically adds the prefix.
|
|
@@ -208,32 +206,34 @@ export function prefixedStringId<Brand extends StringId>() {
|
|
|
208
206
|
*/
|
|
209
207
|
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => ex(str),
|
|
210
208
|
prefix,
|
|
211
|
-
withDefault: schema.pipe(
|
|
209
|
+
withDefault: schema.pipe(S.withConstructorDefault<S.Codec<Type, string> & S.WithoutConstructorDefault>(
|
|
210
|
+
Effect.sync(make)
|
|
211
|
+
))
|
|
212
212
|
})
|
|
213
213
|
)
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
export const brandedStringId = <
|
|
218
|
-
|
|
218
|
+
Id
|
|
219
219
|
>() =>
|
|
220
220
|
withDefaultMake(
|
|
221
|
-
Object.assign(Object.create(StringId), StringId) as S.Codec<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
} & WithDefaults<S.Codec<
|
|
221
|
+
Object.assign(Object.create(StringId), StringId) as S.Codec<Id, string> & {
|
|
222
|
+
withDefault: S.withConstructorDefault<S.Codec<Id, string> & S.WithoutConstructorDefault>
|
|
223
|
+
make: () => Id
|
|
224
|
+
} & WithDefaults<S.Codec<Id, string>>
|
|
225
225
|
)
|
|
226
226
|
|
|
227
227
|
export interface PrefixedStringUtils<
|
|
228
|
-
|
|
228
|
+
Type extends StringId,
|
|
229
229
|
Prefix extends string,
|
|
230
230
|
Separator extends string
|
|
231
231
|
> {
|
|
232
|
-
readonly make: () =>
|
|
233
|
-
readonly unsafeFrom: (str: string) =>
|
|
234
|
-
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) =>
|
|
232
|
+
readonly make: () => Type
|
|
233
|
+
readonly unsafeFrom: (str: string) => Type
|
|
234
|
+
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => Type
|
|
235
235
|
readonly prefix: Prefix
|
|
236
|
-
readonly withDefault: S.withConstructorDefault<S.Codec<
|
|
236
|
+
readonly withDefault: S.withConstructorDefault<S.Codec<Type, string> & S.WithoutConstructorDefault>
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
export interface UrlBrand extends Simplify<B.Brand<"Url"> & NonEmptyStringBrand> {}
|
|
@@ -247,9 +247,12 @@ const isUrl: Refinement<string, Url> = (s: string): s is Url => {
|
|
|
247
247
|
export const Url = S
|
|
248
248
|
.String
|
|
249
249
|
.pipe(
|
|
250
|
+
S.annotate({
|
|
251
|
+
title: "Url",
|
|
252
|
+
format: "uri"
|
|
253
|
+
}),
|
|
250
254
|
S.refine(isUrl, {
|
|
251
255
|
identifier: "Url",
|
|
252
|
-
title: "Url",
|
|
253
256
|
jsonSchema: { format: "uri" }
|
|
254
257
|
}),
|
|
255
258
|
S.annotate({
|