effect-app 4.0.0-beta.33 → 4.0.0-beta.331
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 +1628 -0
- package/dist/Array.d.ts +2 -1
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +4 -4
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Config/SecretURL.d.ts +3 -1
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +3 -6
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- package/dist/Config/internal/configSecretURL.js +2 -2
- package/dist/Config.d.ts +2 -0
- package/dist/Config.d.ts.map +1 -0
- package/dist/Config.js +8 -0
- package/dist/ConfigProvider.d.ts +2 -0
- package/dist/ConfigProvider.d.ts.map +1 -0
- package/dist/ConfigProvider.js +6 -0
- package/dist/{ServiceMap.d.ts → Context.d.ts} +18 -20
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +67 -0
- package/dist/DataDependencies.d.ts +136 -0
- package/dist/DataDependencies.d.ts.map +1 -0
- package/dist/DataDependencies.js +112 -0
- package/dist/Effect.d.ts +10 -9
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +6 -9
- package/dist/Emailer.d.ts +51 -0
- package/dist/Emailer.d.ts.map +1 -0
- package/dist/Emailer.js +7 -0
- package/dist/Function.d.ts.map +1 -1
- package/dist/Layer.d.ts +10 -6
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +3 -2
- package/dist/Model/Repository/Registry.d.ts +22 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +18 -0
- package/dist/Model/Repository/dependency.d.ts +6 -0
- package/dist/Model/Repository/dependency.d.ts.map +1 -0
- package/dist/Model/Repository/dependency.js +29 -0
- package/dist/Model/Repository/ext.d.ts +60 -0
- package/dist/Model/Repository/ext.d.ts.map +1 -0
- package/dist/Model/Repository/ext.js +124 -0
- package/dist/Model/Repository/internal/internal.d.ts +68 -0
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
- package/dist/Model/Repository/internal/internal.js +611 -0
- package/dist/Model/Repository/legacy.d.ts +21 -0
- package/dist/Model/Repository/legacy.d.ts.map +1 -0
- package/dist/Model/Repository/legacy.js +2 -0
- package/dist/Model/Repository/makeRepo.d.ts +71 -0
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
- package/dist/Model/Repository/makeRepo.js +30 -0
- package/dist/Model/Repository/service.d.ts +121 -0
- package/dist/Model/Repository/service.d.ts.map +1 -0
- package/dist/Model/Repository/service.js +2 -0
- package/dist/Model/Repository/validation.d.ts +72 -0
- package/dist/Model/Repository/validation.d.ts.map +1 -0
- package/dist/Model/Repository/validation.js +39 -0
- package/dist/Model/Repository.d.ts +8 -0
- package/dist/Model/Repository.d.ts.map +1 -0
- package/dist/Model/Repository.js +8 -0
- package/dist/Model/dsl.d.ts +33 -0
- package/dist/Model/dsl.d.ts.map +1 -0
- package/dist/Model/dsl.js +43 -0
- package/dist/Model/filter/filterApi.d.ts +30 -0
- package/dist/Model/filter/filterApi.d.ts.map +1 -0
- package/dist/Model/filter/filterApi.js +2 -0
- package/dist/Model/filter/types/errors.d.ts +29 -0
- package/dist/Model/filter/types/errors.d.ts.map +1 -0
- package/dist/Model/filter/types/errors.js +2 -0
- package/dist/Model/filter/types/fields.d.ts +15 -0
- package/dist/Model/filter/types/fields.d.ts.map +1 -0
- package/dist/Model/filter/types/fields.js +2 -0
- package/dist/Model/filter/types/path/common.d.ts +316 -0
- package/dist/Model/filter/types/path/common.d.ts.map +1 -0
- package/dist/Model/filter/types/path/common.js +2 -0
- package/dist/Model/filter/types/path/eager.d.ts +94 -0
- package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
- package/dist/Model/filter/types/path/eager.js +36 -0
- package/dist/Model/filter/types/path/index.d.ts +4 -0
- package/dist/Model/filter/types/path/index.d.ts.map +1 -0
- package/dist/Model/filter/types/path/index.js +3 -0
- package/dist/Model/filter/types/utils.d.ts +79 -0
- package/dist/Model/filter/types/utils.d.ts.map +1 -0
- package/dist/Model/filter/types/utils.js +2 -0
- package/dist/Model/filter/types/validator.d.ts +30 -0
- package/dist/Model/filter/types/validator.d.ts.map +1 -0
- package/dist/Model/filter/types/validator.js +2 -0
- package/dist/Model/filter/types.d.ts +5 -0
- package/dist/Model/filter/types.d.ts.map +1 -0
- package/dist/Model/filter/types.js +7 -0
- package/dist/Model/query/dsl.d.ts +630 -0
- package/dist/Model/query/dsl.d.ts.map +1 -0
- package/dist/Model/query/dsl.js +376 -0
- package/dist/Model/query/new-kid-interpreter.d.ts +132 -0
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
- package/dist/Model/query/new-kid-interpreter.js +370 -0
- package/dist/Model/query.d.ts +15 -0
- package/dist/Model/query.d.ts.map +1 -0
- package/dist/Model/query.js +3 -0
- package/dist/Model.d.ts +6 -0
- package/dist/Model.d.ts.map +1 -0
- package/dist/Model.js +6 -0
- package/dist/NonEmptySet.d.ts +3 -1
- package/dist/NonEmptySet.d.ts.map +1 -1
- package/dist/NonEmptySet.js +2 -2
- package/dist/Option.d.ts +1 -0
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +3 -1
- package/dist/Pure.d.ts +7 -5
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +7 -4
- package/dist/QueueMaker.d.ts +11 -0
- package/dist/QueueMaker.d.ts.map +1 -0
- package/dist/QueueMaker.js +11 -0
- package/dist/RequestContext.d.ts +87 -0
- package/dist/RequestContext.d.ts.map +1 -0
- package/dist/RequestContext.js +45 -0
- package/dist/RequestScopedDependencies.d.ts +21 -0
- package/dist/RequestScopedDependencies.d.ts.map +1 -0
- package/dist/RequestScopedDependencies.js +16 -0
- package/dist/Schema/Class.d.ts +200 -20
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +268 -17
- package/dist/Schema/FastCheck.d.ts.map +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 +34 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +117 -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 +5 -5
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +3 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +12 -6
- package/dist/Schema/ext.d.ts +335 -61
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +388 -63
- package/dist/Schema/moreStrings.d.ts +82 -36
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +90 -53
- package/dist/Schema/numbers.d.ts +33 -21
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +65 -12
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +11 -7
- package/dist/Schema/schema.d.ts +1 -1
- package/dist/Schema/schema.d.ts.map +1 -1
- package/dist/Schema/schema.js +2 -2
- package/dist/Schema/strings.d.ts +18 -4
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -5
- package/dist/Schema.d.ts +225 -7
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +193 -26
- package/dist/SchemaAST.d.ts +2 -0
- package/dist/SchemaAST.d.ts.map +1 -0
- package/dist/SchemaAST.js +2 -0
- package/dist/Set.d.ts +4 -1
- package/dist/Set.d.ts.map +1 -1
- package/dist/Set.js +3 -2
- package/dist/Store.d.ts +215 -0
- package/dist/Store.d.ts.map +1 -0
- package/dist/Store.js +113 -0
- package/dist/_ext/Array.d.ts +1 -1
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/Array.js +4 -2
- package/dist/_ext/misc.d.ts +4 -1
- package/dist/_ext/misc.d.ts.map +1 -1
- package/dist/_ext/misc.js +4 -2
- package/dist/_ext/ord.ext.d.ts +2 -1
- package/dist/_ext/ord.ext.d.ts.map +1 -1
- package/dist/_ext/ord.ext.js +2 -2
- package/dist/builtin.d.ts +2 -14
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +1 -1
- package/dist/client/InvalidationKeys.d.ts +30 -0
- package/dist/client/InvalidationKeys.d.ts.map +1 -0
- package/dist/client/InvalidationKeys.js +37 -0
- package/dist/client/apiClientFactory.d.ts +19 -31
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +157 -39
- package/dist/client/clientFor.d.ts +54 -18
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +82 -27
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +75 -19
- package/dist/client/makeClient.d.ts +490 -34
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +62 -30
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/faker.d.ts.map +1 -1
- package/dist/http/Request.d.ts +1 -1
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +2 -2
- package/dist/ids.d.ts +32 -14
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +38 -6
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/middleware.d.ts +13 -7
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +14 -8
- package/dist/rpc/Invalidation.d.ts +1124 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +178 -0
- package/dist/rpc/MiddlewareMaker.d.ts +11 -7
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +59 -38
- 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 +14 -10
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/rpc.d.ts +1 -1
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -2
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +40 -0
- package/dist/setupRequest.d.ts +20 -0
- package/dist/setupRequest.d.ts.map +1 -0
- package/dist/setupRequest.js +72 -0
- package/dist/toast.d.ts +51 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +34 -0
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +4 -5
- package/dist/utils/effectify.d.ts +1 -1
- package/dist/utils/effectify.d.ts.map +1 -1
- package/dist/utils/effectify.js +2 -2
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/gen.d.ts +4 -4
- 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 +4 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +4 -4
- package/dist/utils.d.ts +34 -39
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -37
- package/dist/validation/validators.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/withToast.d.ts +30 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +64 -0
- package/package.json +27 -251
- package/src/Array.ts +5 -5
- package/src/Chunk.ts +3 -3
- package/src/Config/SecretURL.ts +6 -3
- package/src/Config/internal/configSecretURL.ts +2 -2
- package/src/Config.ts +7 -0
- package/src/ConfigProvider.ts +5 -0
- package/src/{ServiceMap.ts → Context.ts} +56 -63
- package/src/DataDependencies.ts +171 -0
- package/src/Effect.ts +15 -17
- package/src/Emailer.ts +51 -0
- package/src/Inputify.type.ts +1 -1
- package/src/Layer.ts +11 -7
- package/src/Model/Repository/Registry.ts +35 -0
- package/src/Model/Repository/dependency.ts +32 -0
- package/src/Model/Repository/ext.ts +374 -0
- package/src/Model/Repository/internal/internal.ts +988 -0
- package/src/Model/Repository/legacy.ts +29 -0
- package/src/Model/Repository/makeRepo.ts +168 -0
- package/src/Model/Repository/service.ts +676 -0
- package/src/Model/Repository/validation.ts +38 -0
- package/src/Model/Repository.ts +7 -0
- package/src/Model/dsl.ts +129 -0
- package/src/Model/filter/filterApi.ts +78 -0
- package/src/Model/filter/types/errors.ts +47 -0
- package/src/Model/filter/types/fields.ts +50 -0
- package/src/Model/filter/types/path/common.ts +404 -0
- package/src/Model/filter/types/path/eager.ts +329 -0
- package/src/Model/filter/types/path/index.ts +4 -0
- package/src/Model/filter/types/utils.ts +128 -0
- package/src/Model/filter/types/validator.ts +46 -0
- package/src/Model/filter/types.ts +6 -0
- package/src/Model/query/dsl.ts +3092 -0
- package/src/Model/query/new-kid-interpreter.ts +532 -0
- package/src/Model/query.ts +13 -0
- package/src/Model.ts +5 -0
- package/src/NonEmptySet.ts +6 -4
- package/src/Option.ts +2 -0
- package/src/Pure.ts +10 -7
- package/src/QueueMaker.ts +11 -0
- package/src/RequestContext.ts +48 -0
- package/src/RequestScopedDependencies.ts +45 -0
- package/src/Schema/Class.ts +648 -60
- 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 +24 -9
- package/src/Schema/email.ts +14 -7
- package/src/Schema/ext.ts +508 -102
- package/src/Schema/moreStrings.ts +217 -86
- package/src/Schema/numbers.ts +107 -28
- package/src/Schema/phoneNumber.ts +13 -9
- package/src/Schema/schema.ts +1 -1
- package/src/Schema/strings.ts +23 -14
- package/src/Schema.ts +403 -46
- package/src/SchemaAST.ts +1 -0
- package/src/Set.ts +6 -2
- package/src/Store.ts +325 -0
- package/src/_ext/Array.ts +4 -2
- package/src/_ext/misc.ts +4 -1
- package/src/_ext/ord.ext.ts +2 -1
- package/src/builtin-json.d.ts +24 -0
- package/src/builtin.ts +16 -21
- package/src/client/InvalidationKeys.ts +55 -0
- package/src/client/apiClientFactory.ts +301 -141
- package/src/client/clientFor.ts +107 -34
- package/src/client/errors.ts +100 -29
- package/src/client/makeClient.ts +594 -80
- package/src/client.ts +6 -4
- package/src/http/Request.ts +3 -3
- package/src/http.ts +1 -1
- package/src/ids.ts +45 -7
- package/src/index.ts +20 -23
- package/src/middleware.ts +13 -9
- package/src/rpc/Invalidation.ts +278 -0
- package/src/rpc/MiddlewareMaker.ts +88 -80
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +7 -6
- package/src/rpc/RpcMiddleware.ts +19 -13
- package/src/rpc.ts +4 -4
- package/src/runtime.ts +57 -0
- package/src/setupRequest.ts +138 -0
- package/src/toast.ts +54 -0
- package/src/transform.ts +4 -4
- package/src/utils/effectify.ts +1 -1
- package/src/utils/gen.ts +8 -8
- package/src/utils/logLevel.ts +1 -1
- package/src/utils/logger.ts +4 -3
- package/src/utils.ts +85 -158
- package/src/validation.ts +2 -2
- package/src/withToast.ts +133 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/rpcMiddlewareContext.test.d.ts.map +1 -0
- 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 +55 -10
- package/test/rpc.test.ts +38 -10
- package/test/rpcMiddlewareContext.test.ts +63 -0
- package/test/schema.test.ts +505 -26
- package/test/secretURL.test.ts +160 -0
- package/test/special.test.ts +1261 -0
- package/test/utils.test.ts +7 -7
- package/tsconfig.base.json +6 -5
- package/tsconfig.json +9 -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.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
- package/tsconfig.json.bak +0 -12
package/src/Schema/ext.ts
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* # `withConstructorDefault` policy
|
|
5
|
+
*
|
|
6
|
+
* The `withConstructorDefault` properties exported throughout this module
|
|
7
|
+
* (and from `numbers.ts`, `moreStrings.ts`, `ids.ts`) attach a default value
|
|
8
|
+
* that is **only** applied during construction — i.e. when the field is
|
|
9
|
+
* omitted from the input to a Schema constructor / `.make(...)` call.
|
|
10
|
+
*
|
|
11
|
+
* They are **NOT** applied during `decode` (JSON, database rows, RPC payloads,
|
|
12
|
+
* etc.). Decoding a payload with a missing field will still fail with a parse
|
|
13
|
+
* error, exactly as if the default were not present.
|
|
14
|
+
*
|
|
15
|
+
* Concretely this means `withConstructorDefault` MUST NOT be relied on as a
|
|
16
|
+
* just-in-time migration mechanism for database fields. If a stored record is
|
|
17
|
+
* missing a newly added field, the constructor default will not fill it in on
|
|
18
|
+
* read — decoding will fail.
|
|
19
|
+
*
|
|
20
|
+
* ## Don't reach for `withDecodingDefault*` either
|
|
21
|
+
*
|
|
22
|
+
* The sibling `withDecodingDefaultType` (and `withDecodingDefault`) extensions
|
|
23
|
+
* exist, but they are discouraged for migrating persisted data. A missing
|
|
24
|
+
* field in a stored record is just as likely to be data corruption as it is
|
|
25
|
+
* an old-shape document; silently substituting a default hides the problem
|
|
26
|
+
* and can poison downstream aggregates.
|
|
27
|
+
*
|
|
28
|
+
* Prefer an **explicit, preferably versioned** migration of database data
|
|
29
|
+
* (a schema-version field, a one-shot backfill, or a transform on read that
|
|
30
|
+
* is gated on an explicit version marker) over shoving missing fields under
|
|
31
|
+
* the rug with a decode-time default.
|
|
32
|
+
*/
|
|
33
|
+
import * as Config from "effect/Config"
|
|
34
|
+
import * as Effect from "effect/Effect"
|
|
35
|
+
import { pipe } from "effect/Function"
|
|
36
|
+
import * as Function from "effect/Function"
|
|
37
|
+
import * as Option from "effect/Option"
|
|
38
|
+
import * as Order from "effect/Order"
|
|
4
39
|
import * as S from "effect/Schema"
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
40
|
+
import * as SchemaIssue from "effect/SchemaIssue"
|
|
41
|
+
import * as SchemaTransformation from "effect/SchemaTransformation"
|
|
42
|
+
import * as Struct from "effect/Struct"
|
|
43
|
+
import { type NonEmptyReadonlyArray } from "../Array.ts"
|
|
44
|
+
import * as Context from "../Context.ts"
|
|
45
|
+
import * as SchemaAST from "../SchemaAST.ts"
|
|
46
|
+
import { extendM, typedKeysOf } from "../utils.ts"
|
|
47
|
+
import { type AST } from "./schema.ts"
|
|
8
48
|
|
|
9
49
|
type ProvidedCodec<Self extends S.Top, R> = S.Codec<
|
|
10
50
|
Self["Type"],
|
|
@@ -13,137 +53,417 @@ type ProvidedCodec<Self extends S.Top, R> = S.Codec<
|
|
|
13
53
|
Exclude<Self["EncodingServices"], R>
|
|
14
54
|
>
|
|
15
55
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export const withDefaultConstructor = <A>(
|
|
21
|
-
makeDefault: () => NoInfer<A>
|
|
22
|
-
) =>
|
|
23
|
-
<Self extends S.Top>(self: Self): S.withConstructorDefault<Self & S.WithoutConstructorDefault> => {
|
|
24
|
-
type Narrowed = Self & S.WithoutConstructorDefault
|
|
25
|
-
return S.withConstructorDefault<Narrowed>(
|
|
26
|
-
() => Option.some(makeDefault() as Narrowed["~type.make.in"])
|
|
27
|
-
)(self as Narrowed)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// TODO: v4 migration - Date is no longer by default encoded to string.
|
|
31
|
-
const DateFromString = Schema.Date.pipe(
|
|
32
|
-
Schema.encodeTo(Schema.String, {
|
|
33
|
-
decode: SchemaGetter.Date(),
|
|
34
|
-
encode: SchemaGetter.transform((_) => _.toISOString())
|
|
35
|
-
})
|
|
56
|
+
const concurrencySetting = Effect.runSync(
|
|
57
|
+
Config
|
|
58
|
+
.Literal("unbounded", "SCHEMA_CONCURRENCY")
|
|
59
|
+
.pipe(Config.orElse(() => Config.Number("SCHEMA_CONCURRENCY")), Config.option)
|
|
36
60
|
)
|
|
37
61
|
|
|
62
|
+
export const DefaultParseOptions: SchemaAST.ParseOptions = {
|
|
63
|
+
concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const)
|
|
64
|
+
}
|
|
65
|
+
|
|
38
66
|
/**
|
|
39
|
-
*
|
|
67
|
+
* Parse-options annotation used on schema constructors for decode paths where callers
|
|
68
|
+
* cannot currently pass parse options (notably some RPC / HttpApi integration paths).
|
|
69
|
+
*
|
|
70
|
+
* Keep this annotation in place so those framework-managed decodes still run with
|
|
71
|
+
* unbounded concurrency by default.
|
|
40
72
|
*/
|
|
41
|
-
export const
|
|
42
|
-
|
|
73
|
+
export const concurrencyUnbounded = { parseOptions: DefaultParseOptions } as const
|
|
74
|
+
|
|
75
|
+
type DecodeLike = (schema: any) => (input: any, options?: SchemaAST.ParseOptions) => any
|
|
76
|
+
|
|
77
|
+
export const withDefaultParseOptions = <Decode extends DecodeLike>(
|
|
78
|
+
decode: Decode,
|
|
79
|
+
defaultParseOptions: SchemaAST.ParseOptions = DefaultParseOptions
|
|
80
|
+
): Decode =>
|
|
81
|
+
((schema: any) => {
|
|
82
|
+
const run = decode(schema)
|
|
83
|
+
return (input: any, options?: SchemaAST.ParseOptions) => run(input, { ...defaultParseOptions, ...options })
|
|
84
|
+
}) as Decode
|
|
85
|
+
|
|
86
|
+
const DateString = S.String.annotate({
|
|
87
|
+
identifier: "DateOrInvalid",
|
|
88
|
+
description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)",
|
|
89
|
+
format: "date-time"
|
|
43
90
|
})
|
|
44
91
|
|
|
45
92
|
/**
|
|
46
|
-
*
|
|
93
|
+
* Schema type for {@link DateFromString}.
|
|
94
|
+
*
|
|
95
|
+
* @category Schemas
|
|
96
|
+
* @since 4.0.0
|
|
47
97
|
*/
|
|
48
|
-
export
|
|
49
|
-
withDefault: S.Boolean.pipe(withDefaultConstructor(() => false))
|
|
50
|
-
})
|
|
98
|
+
export interface DateFromString extends S.decodeTo<S.Date, S.String> {}
|
|
51
99
|
|
|
52
100
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
101
|
+
* A transformation schema that parses an ISO 8601 string into a `Date`.
|
|
102
|
+
*
|
|
103
|
+
* Decoding:
|
|
104
|
+
* - A `string` is decoded as a `Date`.
|
|
105
|
+
*
|
|
106
|
+
* Encoding:
|
|
107
|
+
* - A `Date` is encoded as a `string`.
|
|
108
|
+
*
|
|
109
|
+
* Use this when the Encoded form must be JSON (`string`). Domain models should
|
|
110
|
+
* prefer {@link Date}, whose Encoded form is `Date`; JSON stores convert via
|
|
111
|
+
* `Schema.toCodecJson`.
|
|
112
|
+
*
|
|
113
|
+
* @since 4.0.0
|
|
55
114
|
*/
|
|
56
|
-
export const
|
|
115
|
+
export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString))
|
|
116
|
+
|
|
117
|
+
const dateHelpers = (s: S.Date) => ({
|
|
118
|
+
/**
|
|
119
|
+
* Construction-only default `new Date()`. Applied only when the field is
|
|
120
|
+
* omitted from `.make(...)` input. NOT applied during decode — cannot be
|
|
121
|
+
* used to JIT-migrate database fields. See file-level note.
|
|
122
|
+
*/
|
|
123
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
|
|
124
|
+
/**
|
|
125
|
+
* Decode-time default `new Date()`. **Discouraged for persisted data:** a
|
|
126
|
+
* missing field may be data corruption, not an old-shape document; silently
|
|
127
|
+
* substituting `new Date()` hides the problem. Prefer an explicit,
|
|
128
|
+
* preferably versioned migration over a decode-time fallback. See
|
|
129
|
+
* file-level note.
|
|
130
|
+
*/
|
|
131
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
/** Like the default Schema `Date` (Encoded is `Date`) with default helpers. */
|
|
135
|
+
export const Date = extendM(S.Date, dateHelpers)
|
|
57
136
|
|
|
58
137
|
/**
|
|
59
|
-
*
|
|
138
|
+
* Alias of {@link Date}. Core `Schema.Date` already rejects invalid Dates.
|
|
139
|
+
*
|
|
140
|
+
* @deprecated Use {@link Date}.
|
|
60
141
|
*/
|
|
61
|
-
export const
|
|
142
|
+
export const DateValid = Date
|
|
143
|
+
|
|
144
|
+
/** Like the default Schema `Boolean` but with default helpers. */
|
|
145
|
+
export const Boolean = Object.assign(S.Boolean, {
|
|
146
|
+
/**
|
|
147
|
+
* Construction-only default `false`. Applied only when the field is
|
|
148
|
+
* omitted from `.make(...)` input. NOT applied during decode — cannot be
|
|
149
|
+
* used to JIT-migrate database fields. See file-level note.
|
|
150
|
+
*/
|
|
151
|
+
withConstructorDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
|
|
152
|
+
/**
|
|
153
|
+
* Decode-time default `false`. **Discouraged for persisted data:** a
|
|
154
|
+
* missing field may be data corruption, not an old-shape document; silently
|
|
155
|
+
* substituting `false` hides the problem. Prefer an explicit, preferably
|
|
156
|
+
* versioned migration over a decode-time fallback. See file-level note.
|
|
157
|
+
*/
|
|
158
|
+
withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
|
|
159
|
+
})
|
|
62
160
|
|
|
63
161
|
/**
|
|
64
|
-
*
|
|
162
|
+
* You probably want to use `Finite` instead of this. Like the default Schema
|
|
163
|
+
* `Number` but with default helpers.
|
|
65
164
|
*/
|
|
66
|
-
export const
|
|
165
|
+
export const Number = Object.assign(S.Number, {
|
|
166
|
+
/**
|
|
167
|
+
* Construction-only default `0`. Applied only when the field is omitted
|
|
168
|
+
* from `.make(...)` input. NOT applied during decode — cannot be used to
|
|
169
|
+
* JIT-migrate database fields. See file-level note.
|
|
170
|
+
*/
|
|
171
|
+
withConstructorDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
172
|
+
/**
|
|
173
|
+
* Decode-time default `0`. **Discouraged for persisted data:** a missing
|
|
174
|
+
* field may be data corruption, not an old-shape document; silently
|
|
175
|
+
* substituting `0` hides the problem. Prefer an explicit, preferably
|
|
176
|
+
* versioned migration over a decode-time fallback. See file-level note.
|
|
177
|
+
*/
|
|
178
|
+
withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// Generation-only: native Arbitrary otherwise samples up to ±Number.MAX_VALUE, so
|
|
182
|
+
// derived totals overflow to Infinity. Validation and JSON Schema are unaffected.
|
|
183
|
+
const realisticFiniteSamples = S.makeFilter<number>(() => undefined, {
|
|
184
|
+
arbitraryConstraint: { order: Order.Number, minimum: -1_000_000, maximum: 1_000_000 }
|
|
185
|
+
})
|
|
186
|
+
const FiniteSchema = S.Finite.pipe(S.check(realisticFiniteSamples))
|
|
187
|
+
|
|
188
|
+
/** Like the default Schema `Finite` but with default helpers. */
|
|
189
|
+
export const Finite = Object.assign(FiniteSchema, {
|
|
190
|
+
/**
|
|
191
|
+
* Construction-only default `0`. Applied only when the field is omitted
|
|
192
|
+
* from `.make(...)` input. NOT applied during decode — cannot be used to
|
|
193
|
+
* JIT-migrate database fields. See file-level note.
|
|
194
|
+
*/
|
|
195
|
+
withConstructorDefault: FiniteSchema.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
196
|
+
/**
|
|
197
|
+
* Decode-time default `0`. **Discouraged for persisted data:** a missing
|
|
198
|
+
* field may be data corruption, not an old-shape document; silently
|
|
199
|
+
* substituting `0` hides the problem. Prefer an explicit, preferably
|
|
200
|
+
* versioned migration over a decode-time fallback. See file-level note.
|
|
201
|
+
*/
|
|
202
|
+
withDecodingDefaultType: FiniteSchema.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
/** Like the default Schema `Literals` but with default helpers. Default value is `literals[0]`. */
|
|
206
|
+
export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
|
|
67
207
|
pipe(
|
|
68
208
|
S.Literals(literals),
|
|
69
209
|
(s) =>
|
|
70
210
|
Object.assign(s, {
|
|
211
|
+
/** Override the default literal value used by `withConstructorDefault` / `withDecodingDefaultType`. */
|
|
71
212
|
changeDefault: <A extends Literals[number]>(a: A) => {
|
|
72
213
|
return Object.assign(S.Literals(literals), {
|
|
73
214
|
Default: a,
|
|
74
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Construction-only default. Applied only when the field is
|
|
217
|
+
* omitted from `.make(...)` input. NOT applied during decode —
|
|
218
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
219
|
+
* note.
|
|
220
|
+
*/
|
|
221
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
|
|
222
|
+
/**
|
|
223
|
+
* Decode-time default. **Discouraged for persisted data:** a
|
|
224
|
+
* missing field may be data corruption, not an old-shape
|
|
225
|
+
* document; silently substituting hides the problem. Prefer an
|
|
226
|
+
* explicit, preferably versioned migration over a decode-time
|
|
227
|
+
* fallback. See file-level note.
|
|
228
|
+
*/
|
|
229
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
|
|
75
230
|
}) // todo: copy annotations from original?
|
|
76
231
|
},
|
|
77
|
-
|
|
78
|
-
|
|
232
|
+
// 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
|
|
233
|
+
Default: literals[0] as Literals[0],
|
|
234
|
+
/**
|
|
235
|
+
* Construction-only default `literals[0]`. Applied only when the
|
|
236
|
+
* field is omitted from `.make(...)` input. NOT applied during
|
|
237
|
+
* decode — cannot be used to JIT-migrate database fields. See
|
|
238
|
+
* file-level note.
|
|
239
|
+
*/
|
|
240
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
|
|
241
|
+
/**
|
|
242
|
+
* Decode-time default `literals[0]`. **Discouraged for persisted
|
|
243
|
+
* data:** a missing field may be data corruption, not an old-shape
|
|
244
|
+
* document; silently substituting hides the problem. Prefer an
|
|
245
|
+
* explicit, preferably versioned migration over a decode-time
|
|
246
|
+
* fallback. See file-level note.
|
|
247
|
+
*/
|
|
248
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
|
|
79
249
|
})
|
|
80
250
|
)
|
|
81
251
|
|
|
82
|
-
/**
|
|
83
|
-
* Like the default Schema `Array` but with `withDefault` => []
|
|
84
|
-
*/
|
|
252
|
+
/** Like the default Schema `Array` but with default helpers. */
|
|
85
253
|
export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
86
254
|
return pipe(
|
|
87
|
-
S.Array(value),
|
|
88
|
-
(s) =>
|
|
255
|
+
S.Array(value).annotate(concurrencyUnbounded),
|
|
256
|
+
(s) =>
|
|
257
|
+
Object.assign(s, {
|
|
258
|
+
/**
|
|
259
|
+
* Construction-only default `[]`. Applied only when the field is
|
|
260
|
+
* omitted from `.make(...)` input. NOT applied during decode —
|
|
261
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
262
|
+
* note.
|
|
263
|
+
*/
|
|
264
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
|
|
265
|
+
/**
|
|
266
|
+
* Decode-time default `[]`. **Discouraged for persisted data:** a
|
|
267
|
+
* missing field may be data corruption, not an old-shape document;
|
|
268
|
+
* silently substituting `[]` hides the problem. Prefer an explicit,
|
|
269
|
+
* preferably versioned migration over a decode-time fallback. See
|
|
270
|
+
* file-level note.
|
|
271
|
+
*/
|
|
272
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
|
|
273
|
+
})
|
|
89
274
|
)
|
|
90
275
|
}
|
|
91
276
|
|
|
92
277
|
/**
|
|
93
|
-
*
|
|
278
|
+
* An annotated `S.Array` of unique items that decodes to a `ReadonlySet`.
|
|
94
279
|
*/
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
(
|
|
280
|
+
export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
|
|
281
|
+
const from = S
|
|
282
|
+
.Array(value)
|
|
283
|
+
.annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" })
|
|
284
|
+
const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<ValueSchema["Type"]>>
|
|
285
|
+
const schema = from.pipe(
|
|
286
|
+
S.decodeTo(
|
|
287
|
+
to,
|
|
288
|
+
SchemaTransformation.transform({
|
|
289
|
+
decode: (arr) => new Set(arr) as ReadonlySet<ValueSchema["Type"]>,
|
|
290
|
+
encode: (set) => [...set]
|
|
291
|
+
})
|
|
292
|
+
)
|
|
99
293
|
)
|
|
294
|
+
return schema
|
|
100
295
|
}
|
|
101
296
|
|
|
102
|
-
export { Map_ as Map }
|
|
103
|
-
|
|
104
297
|
/**
|
|
105
|
-
*
|
|
298
|
+
* An annotated `S.Array` of key-value tuples that decodes to a `ReadonlyMap`.
|
|
106
299
|
*/
|
|
300
|
+
export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
301
|
+
readonly key: KeySchema
|
|
302
|
+
readonly value: ValueSchema
|
|
303
|
+
}) => {
|
|
304
|
+
const from = S
|
|
305
|
+
.Array(S.Tuple([pair.key, pair.value]))
|
|
306
|
+
.annotate({
|
|
307
|
+
...concurrencyUnbounded,
|
|
308
|
+
expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
|
|
309
|
+
})
|
|
310
|
+
const to = S.instanceOf(Map) as S.instanceOf<
|
|
311
|
+
ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>
|
|
312
|
+
>
|
|
313
|
+
const schema = from.pipe(
|
|
314
|
+
S.decodeTo(
|
|
315
|
+
to,
|
|
316
|
+
SchemaTransformation.transform({
|
|
317
|
+
decode: (
|
|
318
|
+
arr
|
|
319
|
+
) => new Map(arr) as ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>,
|
|
320
|
+
encode: (
|
|
321
|
+
map
|
|
322
|
+
) => [...map.entries()] as any // fu
|
|
323
|
+
})
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
return schema
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Like the default Schema `ReadonlySet` (Encoded is `Set`) with default helpers. */
|
|
107
330
|
export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
|
|
108
331
|
pipe(
|
|
109
332
|
S.ReadonlySet(value),
|
|
110
333
|
(s) =>
|
|
111
|
-
Object.assign(s, {
|
|
334
|
+
Object.assign(s, {
|
|
335
|
+
/**
|
|
336
|
+
* Construction-only default `new Set()`. Applied only when the field
|
|
337
|
+
* is omitted from `.make(...)` input. NOT applied during decode —
|
|
338
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
339
|
+
* note.
|
|
340
|
+
*/
|
|
341
|
+
withConstructorDefault: s.pipe(
|
|
342
|
+
S.withConstructorDefault(Effect.sync(() => new Set<ValueSchema["Type"]>()))
|
|
343
|
+
),
|
|
344
|
+
/**
|
|
345
|
+
* Decode-time default `new Set()`. **Discouraged for persisted
|
|
346
|
+
* data:** a missing field may be data corruption, not an old-shape
|
|
347
|
+
* document; silently substituting an empty set hides the problem.
|
|
348
|
+
* Prefer an explicit, preferably versioned migration over a
|
|
349
|
+
* decode-time fallback. See file-level note.
|
|
350
|
+
*/
|
|
351
|
+
withDecodingDefaultType: s.pipe(
|
|
352
|
+
S.withDecodingDefaultType(Effect.sync(() => new Set<ValueSchema["Type"]>()))
|
|
353
|
+
)
|
|
354
|
+
})
|
|
112
355
|
)
|
|
113
356
|
|
|
114
|
-
/**
|
|
115
|
-
* Like the default Schema `ReadonlyMap` but with `withDefault` => new Map()
|
|
116
|
-
*/
|
|
357
|
+
/** Like the default Schema `ReadonlyMap` (Encoded is `Map`) with default helpers. */
|
|
117
358
|
export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
118
359
|
readonly key: KeySchema
|
|
119
360
|
readonly value: ValueSchema
|
|
120
361
|
}) =>
|
|
121
362
|
pipe(
|
|
122
363
|
S.ReadonlyMap(pair.key, pair.value),
|
|
123
|
-
(s) =>
|
|
364
|
+
(s) =>
|
|
365
|
+
Object.assign(s, {
|
|
366
|
+
/**
|
|
367
|
+
* Construction-only default `new Map()`. Applied only when the field
|
|
368
|
+
* is omitted from `.make(...)` input. NOT applied during decode —
|
|
369
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
370
|
+
* note.
|
|
371
|
+
*/
|
|
372
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
|
|
373
|
+
/**
|
|
374
|
+
* Decode-time default `new Map()`. **Discouraged for persisted
|
|
375
|
+
* data:** a missing field may be data corruption, not an old-shape
|
|
376
|
+
* document; silently substituting an empty map hides the problem.
|
|
377
|
+
* Prefer an explicit, preferably versioned migration over a
|
|
378
|
+
* decode-time fallback. See file-level note.
|
|
379
|
+
*/
|
|
380
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
|
|
381
|
+
})
|
|
124
382
|
)
|
|
125
383
|
|
|
126
|
-
/**
|
|
127
|
-
* Like the default Schema `NullOr` but with `withDefault` => null
|
|
128
|
-
*/
|
|
384
|
+
/** Like the default Schema `NullOr` but with default helpers. */
|
|
129
385
|
export const NullOr = <Schema extends S.Top>(self: Schema) =>
|
|
130
386
|
pipe(
|
|
131
387
|
S.NullOr(self),
|
|
132
|
-
(s) =>
|
|
388
|
+
(s) =>
|
|
389
|
+
Object.assign(s, {
|
|
390
|
+
/**
|
|
391
|
+
* Construction-only default `null`. Applied only when the field is
|
|
392
|
+
* omitted from `.make(...)` input. NOT applied during decode —
|
|
393
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
394
|
+
* note.
|
|
395
|
+
*/
|
|
396
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
|
|
397
|
+
/**
|
|
398
|
+
* Decode-time default `null`. **Discouraged for persisted data:** a
|
|
399
|
+
* missing field may be data corruption, not an old-shape document;
|
|
400
|
+
* silently substituting `null` hides the problem. Prefer an
|
|
401
|
+
* explicit, preferably versioned migration over a decode-time
|
|
402
|
+
* fallback. See file-level note.
|
|
403
|
+
*/
|
|
404
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
|
|
405
|
+
})
|
|
133
406
|
)
|
|
134
407
|
|
|
135
|
-
|
|
136
|
-
|
|
408
|
+
/**
|
|
409
|
+
* Attach a `withConstructorDefault` of `new Date()` to any schema.
|
|
410
|
+
*
|
|
411
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
412
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
413
|
+
* JIT-migrate database fields. See file-level note.
|
|
414
|
+
*/
|
|
415
|
+
export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
416
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
|
|
137
417
|
|
|
138
|
-
|
|
418
|
+
/**
|
|
419
|
+
* Attach a `withConstructorDefault` of `false` to any schema.
|
|
420
|
+
*
|
|
421
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
422
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
423
|
+
* JIT-migrate database fields. See file-level note.
|
|
424
|
+
*/
|
|
425
|
+
export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
426
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
|
|
139
427
|
|
|
140
|
-
|
|
428
|
+
/**
|
|
429
|
+
* Attach a `withConstructorDefault` of `null` to any schema.
|
|
430
|
+
*
|
|
431
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
432
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
433
|
+
* JIT-migrate database fields. See file-level note.
|
|
434
|
+
*/
|
|
435
|
+
export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
436
|
+
schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
|
|
141
437
|
|
|
142
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Attach a `withConstructorDefault` of `[]` to any schema.
|
|
440
|
+
*
|
|
441
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
442
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
443
|
+
* JIT-migrate database fields. See file-level note.
|
|
444
|
+
*/
|
|
445
|
+
export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
446
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
|
|
143
447
|
|
|
144
|
-
|
|
448
|
+
/**
|
|
449
|
+
* Attach a `withConstructorDefault` of `new Map()` to any schema.
|
|
450
|
+
*
|
|
451
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
452
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
453
|
+
* JIT-migrate database fields. See file-level note.
|
|
454
|
+
*/
|
|
455
|
+
export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
456
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
|
|
145
457
|
|
|
146
|
-
|
|
458
|
+
/**
|
|
459
|
+
* Attach a `withConstructorDefault` of `new Set()` to any schema.
|
|
460
|
+
*
|
|
461
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
462
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
463
|
+
* JIT-migrate database fields. See file-level note.
|
|
464
|
+
*/
|
|
465
|
+
export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
466
|
+
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
|
|
147
467
|
|
|
148
468
|
export const withDefaultMake = <Self extends S.Top>(s: Self) => {
|
|
149
469
|
const a = Object.assign(S.decodeSync(s as any) as WithDefaults<Self>, s)
|
|
@@ -172,9 +492,25 @@ export type WithDefaults<Self extends S.Top> = (
|
|
|
172
492
|
// export type UnionToIntersection3<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I
|
|
173
493
|
// : never
|
|
174
494
|
|
|
495
|
+
/** Union of core `Schema.Date` (Date objects) and string-encoded `DateFromString`, with default helpers. */
|
|
175
496
|
export const inputDate = extendM(
|
|
176
|
-
S.Union([S.
|
|
177
|
-
(s) => ({
|
|
497
|
+
S.Union([S.Date, DateFromString]),
|
|
498
|
+
(s) => ({
|
|
499
|
+
/**
|
|
500
|
+
* Construction-only default `new Date()`. Applied only when the field is
|
|
501
|
+
* omitted from `.make(...)` input. NOT applied during decode — cannot be
|
|
502
|
+
* used to JIT-migrate database fields. See file-level note.
|
|
503
|
+
*/
|
|
504
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
|
|
505
|
+
/**
|
|
506
|
+
* Decode-time default `new Date()`. **Discouraged for persisted data:** a
|
|
507
|
+
* missing field may be data corruption, not an old-shape document;
|
|
508
|
+
* silently substituting `new Date()` hides the problem. Prefer an
|
|
509
|
+
* explicit, preferably versioned migration over a decode-time fallback.
|
|
510
|
+
* See file-level note.
|
|
511
|
+
*/
|
|
512
|
+
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
|
|
513
|
+
})
|
|
178
514
|
)
|
|
179
515
|
|
|
180
516
|
export interface UnionBrand {}
|
|
@@ -192,13 +528,81 @@ export function makeOptional<NER extends S.Struct.Fields>(
|
|
|
192
528
|
}, {} as any)
|
|
193
529
|
}
|
|
194
530
|
|
|
531
|
+
// `Omit` erases the polymorphic `this` of "this-returning" methods (`annotate`, `annotateKey`,
|
|
532
|
+
// `check`, `rebuild`): extracting them through a mapped type binds their `this["Rebuild"]` back to
|
|
533
|
+
// the original `T`, not to this intersection. So overriding just `Rebuild` above is not enough, those
|
|
534
|
+
// four methods have to be redeclared here with a concrete return type instead of `this["Rebuild"]`.
|
|
535
|
+
export type DropConstructorDefault<T extends S.Top> =
|
|
536
|
+
& Omit<
|
|
537
|
+
T,
|
|
538
|
+
"~type.constructor.default" | "Rebuild" | "annotate" | "annotateKey" | "check" | "rebuild"
|
|
539
|
+
>
|
|
540
|
+
& {
|
|
541
|
+
readonly "~type.constructor.default": "no-default"
|
|
542
|
+
readonly "Rebuild": DropConstructorDefault<T>
|
|
543
|
+
annotate(annotations: S.Annotations.Bottom<T["Type"], T["~type.parameters"]>): DropConstructorDefault<T>
|
|
544
|
+
annotateKey(annotations: S.Annotations.Key<T["Type"]>): DropConstructorDefault<T>
|
|
545
|
+
check(
|
|
546
|
+
...checks: readonly [SchemaAST.Check<T["Type"]>, ...Array<SchemaAST.Check<T["Type"]>>]
|
|
547
|
+
): DropConstructorDefault<T>
|
|
548
|
+
rebuild(ast: T["ast"]): DropConstructorDefault<T>
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// `replaceContext` is `@internal` in `effect/SchemaAST` (stripped from its public types, though the
|
|
552
|
+
// export itself still ships in the compiled JS). Effect's own `withConstructorDefault` goes through the
|
|
553
|
+
// same function internally, so this mirrors an existing code path rather than reaching for something
|
|
554
|
+
// novel, but it's still an internal API that could move or disappear without a signal.
|
|
555
|
+
const SchemaASTInternal = SchemaAST as unknown as {
|
|
556
|
+
readonly replaceContext: <A extends SchemaAST.AST>(ast: A, context: SchemaAST.Context | undefined) => A
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export interface DropConstructorDefaultLambda extends Struct.Lambda {
|
|
560
|
+
<T extends S.Top>(self: T): DropConstructorDefault<T>
|
|
561
|
+
readonly "~lambda.out": DropConstructorDefault<this["~lambda.in"] & S.Top>
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* A `Struct.Lambda` that removes a field's `withConstructorDefault`, if any.
|
|
566
|
+
* Compose with `Struct.pick`/`Struct.omit` via `Struct.map` to derive a
|
|
567
|
+
* partial-update schema from a "create" schema without inheriting defaults
|
|
568
|
+
* that would resurrect omitted fields in `.make(...)` output:
|
|
569
|
+
*
|
|
570
|
+
* ```ts
|
|
571
|
+
* import { flow } from "effect/Function"
|
|
572
|
+
* import * as Struct from "effect/Struct"
|
|
573
|
+
* import * as S from "effect-app/Schema"
|
|
574
|
+
*
|
|
575
|
+
* const Person = S.Struct({
|
|
576
|
+
* id: S.String,
|
|
577
|
+
* name: S.String.pipe(S.optionalKey, S.withConstructorDefault(() => ""))
|
|
578
|
+
* })
|
|
579
|
+
*
|
|
580
|
+
* const UpdatePerson = Person.mapFields(
|
|
581
|
+
* flow(Struct.omit(["id"]), Struct.map(S.dropConstructorDefault))
|
|
582
|
+
* )
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
export const dropConstructorDefault: DropConstructorDefaultLambda = Struct.lambda<DropConstructorDefaultLambda>(
|
|
586
|
+
(schema) => {
|
|
587
|
+
const context = schema.ast.context
|
|
588
|
+
if (!context?.constructorDefault) return schema
|
|
589
|
+
return schema.rebuild(
|
|
590
|
+
SchemaASTInternal.replaceContext(
|
|
591
|
+
schema.ast,
|
|
592
|
+
new SchemaAST.Context(context.isOptional, context.isMutable, undefined, context.annotations)
|
|
593
|
+
)
|
|
594
|
+
)
|
|
595
|
+
}
|
|
596
|
+
)
|
|
597
|
+
|
|
195
598
|
export function makeExactOptional<NER extends S.Struct.Fields>(
|
|
196
599
|
t: NER
|
|
197
600
|
): {
|
|
198
|
-
[K in keyof NER]: NER[K] extends S.Top ? ReturnType<typeof S.optionalKey<NER[K] & S.Top
|
|
601
|
+
[K in keyof NER]: NER[K] extends S.Top ? DropConstructorDefault<ReturnType<typeof S.optionalKey<NER[K] & S.Top>>>
|
|
602
|
+
: any
|
|
199
603
|
} {
|
|
200
604
|
return typedKeysOf(t).reduce((prev, cur) => {
|
|
201
|
-
prev[cur] = S.optionalKey(t[cur] as any)
|
|
605
|
+
prev[cur] = dropConstructorDefault(S.optionalKey(t[cur] as any))
|
|
202
606
|
return prev
|
|
203
607
|
}, {} as any)
|
|
204
608
|
}
|
|
@@ -215,20 +619,20 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
|
|
|
215
619
|
from.pipe(
|
|
216
620
|
S.decodeTo(
|
|
217
621
|
to,
|
|
218
|
-
SchemaTransformation.
|
|
622
|
+
SchemaTransformation.transformEffect({
|
|
219
623
|
decode: (input: any, options: any) => Effect.sync(() => decode(input, options)),
|
|
220
624
|
encode: (i: any) =>
|
|
221
625
|
Effect.fail(
|
|
222
626
|
new SchemaIssue.Forbidden(
|
|
223
|
-
|
|
224
|
-
|
|
627
|
+
{ message: "One way schema transformation, encoding is not allowed" },
|
|
628
|
+
i
|
|
225
629
|
)
|
|
226
630
|
)
|
|
227
|
-
})
|
|
631
|
+
})
|
|
228
632
|
)
|
|
229
633
|
)
|
|
230
634
|
|
|
231
|
-
/** A version of
|
|
635
|
+
/** A version of transformEffect which is only a one way mapping of From->To */
|
|
232
636
|
export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
233
637
|
from: From,
|
|
234
638
|
to: To,
|
|
@@ -240,43 +644,45 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
240
644
|
from.pipe(
|
|
241
645
|
S.decodeTo(
|
|
242
646
|
to,
|
|
243
|
-
SchemaTransformation.
|
|
244
|
-
decode
|
|
647
|
+
SchemaTransformation.transformEffect({
|
|
648
|
+
decode,
|
|
245
649
|
encode: (i: any) =>
|
|
246
650
|
Effect.fail(
|
|
247
651
|
new SchemaIssue.Forbidden(
|
|
248
|
-
|
|
249
|
-
|
|
652
|
+
{ message: "One way schema transformation, encoding is not allowed" },
|
|
653
|
+
i
|
|
250
654
|
)
|
|
251
655
|
)
|
|
252
|
-
})
|
|
656
|
+
})
|
|
253
657
|
)
|
|
254
658
|
)
|
|
255
659
|
|
|
256
|
-
export const provide
|
|
257
|
-
self: Self,
|
|
258
|
-
context:
|
|
259
|
-
): ProvidedCodec<Self, R> => {
|
|
660
|
+
export const provide: {
|
|
661
|
+
<R>(context: Context.Context<R>): <Self extends S.Top>(self: Self) => ProvidedCodec<Self, R>
|
|
662
|
+
<Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R>
|
|
663
|
+
} = Function.dual(2, <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R> => {
|
|
260
664
|
const prov = Effect.provide(context)
|
|
261
665
|
return self.pipe(
|
|
262
666
|
S.middlewareDecoding((effect) => prov(effect)),
|
|
263
667
|
S.middlewareEncoding((effect) => prov(effect))
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
export const contextFromServices =
|
|
668
|
+
)
|
|
669
|
+
})
|
|
670
|
+
export const contextFromServices = Effect.fnUntraced(function*<
|
|
267
671
|
Self extends S.Top,
|
|
268
|
-
Tags extends ReadonlyArray<
|
|
672
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
673
|
+
>(self: Self, ...services: Tags) {
|
|
674
|
+
const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
|
|
675
|
+
yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
|
|
676
|
+
)
|
|
677
|
+
return provide(self, context)
|
|
678
|
+
}) as <
|
|
679
|
+
Self extends S.Top,
|
|
680
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
269
681
|
>(
|
|
270
682
|
self: Self,
|
|
271
683
|
...services: Tags
|
|
272
|
-
)
|
|
273
|
-
ProvidedCodec<Self,
|
|
684
|
+
) => Effect.Effect<
|
|
685
|
+
ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
|
|
274
686
|
never,
|
|
275
|
-
|
|
276
|
-
>
|
|
277
|
-
Effect.gen(function*() {
|
|
278
|
-
const context: ServiceMap.ServiceMap<ServiceMap.Service.Identifier<Tags[number]>> = ServiceMap.pick(...services)(
|
|
279
|
-
yield* Effect.services<ServiceMap.Service.Identifier<Tags[number]>>()
|
|
280
|
-
)
|
|
281
|
-
return provide(self, context)
|
|
282
|
-
})
|
|
687
|
+
Context.Service.Identifier<Tags[number]>
|
|
688
|
+
>
|