effect-app 4.0.0-beta.99 → 4.0.0
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 +2028 -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 +2 -2
- 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 +0 -5
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +7 -5
- package/dist/ConfigProvider.d.ts +0 -37
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +5 -41
- package/dist/Context.d.ts +6 -4
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +4 -3
- 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 +3 -3
- 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 +6 -3
- 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 +5 -3
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +5 -2
- 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 +163 -16
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +193 -21
- 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 +15 -2
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
- package/dist/Schema/SpecialJsonSchema.js +70 -12
- package/dist/Schema/SpecialOpenApi.d.ts +1 -1
- package/dist/Schema/SpecialOpenApi.js +3 -3
- package/dist/Schema/brand.d.ts +5 -5
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +6 -4
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +8 -5
- package/dist/Schema/ext.d.ts +297 -72
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +331 -67
- package/dist/Schema/moreStrings.d.ts +82 -48
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +85 -51
- package/dist/Schema/numbers.d.ts +33 -21
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +64 -12
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +6 -6
- 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 +223 -6
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +189 -23
- 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 +13 -8
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +148 -29
- package/dist/client/clientFor.d.ts +48 -13
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +74 -28
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +41 -10
- package/dist/client/makeClient.d.ts +487 -32
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +47 -21
- 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 +2 -2
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +2 -2
- package/dist/http/internal/lib.d.ts +12 -12
- package/dist/http/internal/lib.d.ts.map +1 -1
- package/dist/http/internal/lib.js +13 -13
- package/dist/ids.d.ts +32 -14
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +38 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/middleware.d.ts +12 -6
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +13 -7
- 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 +10 -7
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +57 -37
- package/dist/rpc/RpcContextMap.d.ts +2 -2
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.d.ts +13 -10
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +2 -2
- 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 +6 -1
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/extend.js +24 -6
- 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 +2 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +2 -2
- 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 +26 -266
- 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 +6 -13
- package/src/ConfigProvider.ts +4 -47
- package/src/Context.ts +9 -8
- package/src/DataDependencies.ts +171 -0
- package/src/Effect.ts +9 -12
- package/src/Emailer.ts +51 -0
- package/src/Inputify.type.ts +1 -1
- package/src/Layer.ts +8 -5
- 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 +6 -3
- package/src/QueueMaker.ts +11 -0
- package/src/RequestContext.ts +48 -0
- package/src/RequestScopedDependencies.ts +45 -0
- package/src/Schema/Class.ts +550 -56
- package/src/Schema/SchemaParser.ts +12 -0
- package/src/Schema/SpecialJsonSchema.ts +75 -7
- package/src/Schema/SpecialOpenApi.ts +5 -5
- package/src/Schema/brand.ts +27 -12
- package/src/Schema/email.ts +9 -7
- package/src/Schema/ext.ts +408 -91
- package/src/Schema/moreStrings.ts +213 -88
- package/src/Schema/numbers.ts +107 -29
- package/src/Schema/phoneNumber.ts +8 -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 +291 -126
- package/src/client/clientFor.ts +101 -33
- package/src/client/errors.ts +66 -20
- package/src/client/makeClient.ts +519 -60
- package/src/client.ts +6 -4
- package/src/http/Request.ts +4 -4
- package/src/http/internal/lib.ts +12 -12
- package/src/http.ts +1 -1
- package/src/ids.ts +45 -8
- package/src/index.ts +20 -16
- package/src/middleware.ts +12 -8
- package/src/rpc/Invalidation.ts +278 -0
- package/src/rpc/MiddlewareMaker.ts +86 -79
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +4 -4
- package/src/rpc/RpcMiddleware.ts +18 -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/extend.ts +24 -5
- package/src/utils/gen.ts +7 -7
- package/src/utils/logLevel.ts +1 -1
- package/src/utils/logger.ts +3 -2
- 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/moreStrings.test.ts +55 -10
- package/test/rpc.test.ts +28 -11
- package/test/rpcMiddlewareContext.test.ts +63 -0
- package/test/schema.test.ts +318 -55
- package/test/secretURL.test.ts +4 -1
- package/test/special.test.ts +663 -131
- package/test/utils.test.ts +5 -5
- package/tsconfig.base.json +6 -4
- package/tsconfig.json +9 -1
- package/tsconfig.src.json +29 -29
- package/tsconfig.test.json +2 -2
- package/dist/Operations.d.ts +0 -124
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -29
- 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,12 +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 * as
|
|
8
|
-
import {
|
|
9
|
-
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"
|
|
10
48
|
|
|
11
49
|
type ProvidedCodec<Self extends S.Top, R> = S.Codec<
|
|
12
50
|
Self["Type"],
|
|
@@ -15,11 +53,39 @@ type ProvidedCodec<Self extends S.Top, R> = S.Codec<
|
|
|
15
53
|
Exclude<Self["EncodingServices"], R>
|
|
16
54
|
>
|
|
17
55
|
|
|
18
|
-
|
|
56
|
+
const concurrencySetting = Effect.runSync(
|
|
57
|
+
Config
|
|
58
|
+
.Literal("unbounded", "SCHEMA_CONCURRENCY")
|
|
59
|
+
.pipe(Config.orElse(() => Config.Number("SCHEMA_CONCURRENCY")), Config.option)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
export const DefaultParseOptions: SchemaAST.ParseOptions = {
|
|
63
|
+
concurrency: Option.getOrElse(concurrencySetting, () => "unbounded" as const)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
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.
|
|
72
|
+
*/
|
|
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
|
|
19
85
|
|
|
20
86
|
const DateString = S.String.annotate({
|
|
21
|
-
identifier: "
|
|
22
|
-
description: "
|
|
87
|
+
identifier: "DateOrInvalid",
|
|
88
|
+
description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)",
|
|
23
89
|
format: "date-time"
|
|
24
90
|
})
|
|
25
91
|
|
|
@@ -40,81 +106,169 @@ export interface DateFromString extends S.decodeTo<S.Date, S.String> {}
|
|
|
40
106
|
* Encoding:
|
|
41
107
|
* - A `Date` is encoded as a `string`.
|
|
42
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
|
+
*
|
|
43
113
|
* @since 4.0.0
|
|
44
114
|
*/
|
|
45
115
|
export const DateFromString: DateFromString = DateString.pipe(S.decodeTo(S.Date, SchemaTransformation.dateFromString))
|
|
46
116
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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())))
|
|
53
132
|
})
|
|
54
133
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
*/
|
|
58
|
-
export const DateValid = Object.assign(Date.check(isDateValid()), {
|
|
59
|
-
withDefault: DateFromString.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date()))),
|
|
60
|
-
withDecodingDefaultType: DateFromString.pipe(S.withDecodingDefaultType(Effect.sync(() => new global.Date())))
|
|
61
|
-
})
|
|
134
|
+
/** Like the default Schema `Date` (Encoded is `Date`) with default helpers. */
|
|
135
|
+
export const Date = extendM(S.Date, dateHelpers)
|
|
62
136
|
|
|
63
137
|
/**
|
|
64
|
-
*
|
|
138
|
+
* Alias of {@link Date}. Core `Schema.Date` already rejects invalid Dates.
|
|
139
|
+
*
|
|
140
|
+
* @deprecated Use {@link Date}.
|
|
65
141
|
*/
|
|
142
|
+
export const DateValid = Date
|
|
143
|
+
|
|
144
|
+
/** Like the default Schema `Boolean` but with default helpers. */
|
|
66
145
|
export const Boolean = Object.assign(S.Boolean, {
|
|
67
|
-
|
|
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
|
+
*/
|
|
68
158
|
withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
|
|
69
159
|
})
|
|
70
160
|
|
|
71
161
|
/**
|
|
72
|
-
* You probably want to use `Finite` instead of this.
|
|
73
|
-
*
|
|
162
|
+
* You probably want to use `Finite` instead of this. Like the default Schema
|
|
163
|
+
* `Number` but with default helpers.
|
|
74
164
|
*/
|
|
75
165
|
export const Number = Object.assign(S.Number, {
|
|
76
|
-
|
|
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
|
+
*/
|
|
77
178
|
withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
78
179
|
})
|
|
79
180
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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)))
|
|
86
203
|
})
|
|
87
204
|
|
|
88
|
-
/**
|
|
89
|
-
* Like the default Schema `Literals` but with `withDefault` => literals[0]
|
|
90
|
-
*/
|
|
205
|
+
/** Like the default Schema `Literals` but with default helpers. Default value is `literals[0]`. */
|
|
91
206
|
export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
|
|
92
207
|
pipe(
|
|
93
208
|
S.Literals(literals),
|
|
94
209
|
(s) =>
|
|
95
210
|
Object.assign(s, {
|
|
211
|
+
/** Override the default literal value used by `withConstructorDefault` / `withDecodingDefaultType`. */
|
|
96
212
|
changeDefault: <A extends Literals[number]>(a: A) => {
|
|
97
213
|
return Object.assign(S.Literals(literals), {
|
|
98
214
|
Default: a,
|
|
99
|
-
|
|
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
|
+
*/
|
|
100
229
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
|
|
101
230
|
}) // todo: copy annotations from original?
|
|
102
231
|
},
|
|
103
|
-
|
|
104
|
-
|
|
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
|
+
*/
|
|
105
248
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
|
|
106
249
|
})
|
|
107
250
|
)
|
|
108
251
|
|
|
109
|
-
/**
|
|
110
|
-
* Like the default Schema `Array` but with `withDefault` => []
|
|
111
|
-
*/
|
|
252
|
+
/** Like the default Schema `Array` but with default helpers. */
|
|
112
253
|
export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
113
254
|
return pipe(
|
|
114
|
-
S.Array(value),
|
|
255
|
+
S.Array(value).annotate(concurrencyUnbounded),
|
|
115
256
|
(s) =>
|
|
116
257
|
Object.assign(s, {
|
|
117
|
-
|
|
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
|
+
*/
|
|
118
272
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
|
|
119
273
|
})
|
|
120
274
|
)
|
|
@@ -126,13 +280,13 @@ export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
|
126
280
|
export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
|
|
127
281
|
const from = S
|
|
128
282
|
.Array(value)
|
|
129
|
-
.annotate({ expected: "an array of unique items that will be decoded as a ReadonlySet" })
|
|
130
|
-
const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<
|
|
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"]>>
|
|
131
285
|
const schema = from.pipe(
|
|
132
286
|
S.decodeTo(
|
|
133
287
|
to,
|
|
134
288
|
SchemaTransformation.transform({
|
|
135
|
-
decode: (arr) => new Set(arr) as ReadonlySet<
|
|
289
|
+
decode: (arr) => new Set(arr) as ReadonlySet<ValueSchema["Type"]>,
|
|
136
290
|
encode: (set) => [...set]
|
|
137
291
|
})
|
|
138
292
|
)
|
|
@@ -149,9 +303,12 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
|
|
|
149
303
|
}) => {
|
|
150
304
|
const from = S
|
|
151
305
|
.Array(S.Tuple([pair.key, pair.value]))
|
|
152
|
-
.annotate({
|
|
306
|
+
.annotate({
|
|
307
|
+
...concurrencyUnbounded,
|
|
308
|
+
expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
|
|
309
|
+
})
|
|
153
310
|
const to = S.instanceOf(Map) as S.instanceOf<
|
|
154
|
-
ReadonlyMap<
|
|
311
|
+
ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>
|
|
155
312
|
>
|
|
156
313
|
const schema = from.pipe(
|
|
157
314
|
S.decodeTo(
|
|
@@ -159,7 +316,7 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
|
|
|
159
316
|
SchemaTransformation.transform({
|
|
160
317
|
decode: (
|
|
161
318
|
arr
|
|
162
|
-
) => new Map(arr) as ReadonlyMap<
|
|
319
|
+
) => new Map(arr) as ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>,
|
|
163
320
|
encode: (
|
|
164
321
|
map
|
|
165
322
|
) => [...map.entries()] as any // fu
|
|
@@ -169,65 +326,142 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
|
|
|
169
326
|
return schema
|
|
170
327
|
}
|
|
171
328
|
|
|
172
|
-
/**
|
|
173
|
-
* Like the default Schema `ReadonlySet` but from Array with `withDefault` => new Set()
|
|
174
|
-
*/
|
|
329
|
+
/** Like the default Schema `ReadonlySet` (Encoded is `Set`) with default helpers. */
|
|
175
330
|
export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
|
|
176
331
|
pipe(
|
|
177
|
-
|
|
332
|
+
S.ReadonlySet(value),
|
|
178
333
|
(s) =>
|
|
179
334
|
Object.assign(s, {
|
|
180
|
-
|
|
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
|
+
*/
|
|
181
351
|
withDecodingDefaultType: s.pipe(
|
|
182
|
-
S.withDecodingDefaultType(Effect.sync(() => new Set<
|
|
352
|
+
S.withDecodingDefaultType(Effect.sync(() => new Set<ValueSchema["Type"]>()))
|
|
183
353
|
)
|
|
184
354
|
})
|
|
185
355
|
)
|
|
186
356
|
|
|
187
|
-
/**
|
|
188
|
-
* Like the default Schema `ReadonlyMap` but from Array with `withDefault` => new Map()
|
|
189
|
-
*/
|
|
357
|
+
/** Like the default Schema `ReadonlyMap` (Encoded is `Map`) with default helpers. */
|
|
190
358
|
export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
191
359
|
readonly key: KeySchema
|
|
192
360
|
readonly value: ValueSchema
|
|
193
361
|
}) =>
|
|
194
362
|
pipe(
|
|
195
|
-
|
|
363
|
+
S.ReadonlyMap(pair.key, pair.value),
|
|
196
364
|
(s) =>
|
|
197
365
|
Object.assign(s, {
|
|
198
|
-
|
|
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
|
+
*/
|
|
199
380
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
|
|
200
381
|
})
|
|
201
382
|
)
|
|
202
383
|
|
|
203
|
-
/**
|
|
204
|
-
* Like the default Schema `NullOr` but with `withDefault` => null
|
|
205
|
-
*/
|
|
384
|
+
/** Like the default Schema `NullOr` but with default helpers. */
|
|
206
385
|
export const NullOr = <Schema extends S.Top>(self: Schema) =>
|
|
207
386
|
pipe(
|
|
208
387
|
S.NullOr(self),
|
|
209
388
|
(s) =>
|
|
210
389
|
Object.assign(s, {
|
|
211
|
-
|
|
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
|
+
*/
|
|
212
404
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
|
|
213
405
|
})
|
|
214
406
|
)
|
|
215
407
|
|
|
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
|
+
*/
|
|
216
415
|
export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
217
416
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
|
|
218
417
|
|
|
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
|
+
*/
|
|
219
425
|
export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
220
426
|
schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
|
|
221
427
|
|
|
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
|
+
*/
|
|
222
435
|
export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
223
436
|
schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
|
|
224
437
|
|
|
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
|
+
*/
|
|
225
445
|
export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
226
446
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
|
|
227
447
|
|
|
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
|
+
*/
|
|
228
455
|
export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
229
456
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
|
|
230
457
|
|
|
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
|
+
*/
|
|
231
465
|
export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
232
466
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
|
|
233
467
|
|
|
@@ -258,10 +492,23 @@ export type WithDefaults<Self extends S.Top> = (
|
|
|
258
492
|
// export type UnionToIntersection3<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I
|
|
259
493
|
// : never
|
|
260
494
|
|
|
495
|
+
/** Union of core `Schema.Date` (Date objects) and string-encoded `DateFromString`, with default helpers. */
|
|
261
496
|
export const inputDate = extendM(
|
|
262
|
-
S.Union([S.
|
|
497
|
+
S.Union([S.Date, DateFromString]),
|
|
263
498
|
(s) => ({
|
|
264
|
-
|
|
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
|
+
*/
|
|
265
512
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
|
|
266
513
|
})
|
|
267
514
|
)
|
|
@@ -281,13 +528,81 @@ export function makeOptional<NER extends S.Struct.Fields>(
|
|
|
281
528
|
}, {} as any)
|
|
282
529
|
}
|
|
283
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
|
+
|
|
284
598
|
export function makeExactOptional<NER extends S.Struct.Fields>(
|
|
285
599
|
t: NER
|
|
286
600
|
): {
|
|
287
|
-
[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
|
|
288
603
|
} {
|
|
289
604
|
return typedKeysOf(t).reduce((prev, cur) => {
|
|
290
|
-
prev[cur] = S.optionalKey(t[cur] as any)
|
|
605
|
+
prev[cur] = dropConstructorDefault(S.optionalKey(t[cur] as any))
|
|
291
606
|
return prev
|
|
292
607
|
}, {} as any)
|
|
293
608
|
}
|
|
@@ -304,20 +619,20 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
|
|
|
304
619
|
from.pipe(
|
|
305
620
|
S.decodeTo(
|
|
306
621
|
to,
|
|
307
|
-
SchemaTransformation.
|
|
622
|
+
SchemaTransformation.transformEffect({
|
|
308
623
|
decode: (input: any, options: any) => Effect.sync(() => decode(input, options)),
|
|
309
624
|
encode: (i: any) =>
|
|
310
625
|
Effect.fail(
|
|
311
626
|
new SchemaIssue.Forbidden(
|
|
312
|
-
|
|
313
|
-
|
|
627
|
+
{ message: "One way schema transformation, encoding is not allowed" },
|
|
628
|
+
i
|
|
314
629
|
)
|
|
315
630
|
)
|
|
316
631
|
})
|
|
317
632
|
)
|
|
318
633
|
)
|
|
319
634
|
|
|
320
|
-
/** A version of
|
|
635
|
+
/** A version of transformEffect which is only a one way mapping of From->To */
|
|
321
636
|
export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
322
637
|
from: From,
|
|
323
638
|
to: To,
|
|
@@ -329,43 +644,45 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
329
644
|
from.pipe(
|
|
330
645
|
S.decodeTo(
|
|
331
646
|
to,
|
|
332
|
-
SchemaTransformation.
|
|
647
|
+
SchemaTransformation.transformEffect({
|
|
333
648
|
decode,
|
|
334
649
|
encode: (i: any) =>
|
|
335
650
|
Effect.fail(
|
|
336
651
|
new SchemaIssue.Forbidden(
|
|
337
|
-
|
|
338
|
-
|
|
652
|
+
{ message: "One way schema transformation, encoding is not allowed" },
|
|
653
|
+
i
|
|
339
654
|
)
|
|
340
655
|
)
|
|
341
656
|
})
|
|
342
657
|
)
|
|
343
658
|
)
|
|
344
659
|
|
|
345
|
-
export const provide
|
|
346
|
-
self: Self,
|
|
347
|
-
context: Context.Context<R>
|
|
348
|
-
): 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> => {
|
|
349
664
|
const prov = Effect.provide(context)
|
|
350
665
|
return self.pipe(
|
|
351
666
|
S.middlewareDecoding((effect) => prov(effect)),
|
|
352
667
|
S.middlewareEncoding((effect) => prov(effect))
|
|
353
|
-
)
|
|
354
|
-
}
|
|
355
|
-
export const contextFromServices =
|
|
668
|
+
)
|
|
669
|
+
})
|
|
670
|
+
export const contextFromServices = Effect.fnUntraced(function*<
|
|
671
|
+
Self extends S.Top,
|
|
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 <
|
|
356
679
|
Self extends S.Top,
|
|
357
680
|
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
358
681
|
>(
|
|
359
682
|
self: Self,
|
|
360
683
|
...services: Tags
|
|
361
|
-
)
|
|
684
|
+
) => Effect.Effect<
|
|
362
685
|
ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
|
|
363
686
|
never,
|
|
364
687
|
Context.Service.Identifier<Tags[number]>
|
|
365
|
-
>
|
|
366
|
-
Effect.gen(function*() {
|
|
367
|
-
const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
|
|
368
|
-
yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
|
|
369
|
-
)
|
|
370
|
-
return provide(self, context)
|
|
371
|
-
})
|
|
688
|
+
>
|