effect-app 4.0.0-beta.99 → 4.0.1
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 +2042 -0
- package/dist/Array.d.ts +1 -17
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +3 -27
- package/dist/Chunk.d.ts +3 -27
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Chunk.js +4 -39
- 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/DateTime.d.ts +33 -0
- package/dist/DateTime.d.ts.map +1 -0
- package/dist/DateTime.js +18 -0
- package/dist/Effect.d.ts +3 -14
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +8 -26
- 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 +0 -4
- package/dist/Function.d.ts.map +1 -1
- package/dist/Function.js +1 -7
- 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 +610 -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 +34 -0
- package/dist/Model/dsl.d.ts.map +1 -0
- package/dist/Model/dsl.js +45 -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 +369 -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 -2
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +3 -7
- package/dist/Pure.d.ts +9 -7
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +20 -17
- 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 +279 -75
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +330 -72
- 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 +224 -7
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +190 -24
- 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 +8 -6
- 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/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 +154 -34
- 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/migration/nativeEffect.d.ts +21 -0
- package/dist/migration/nativeEffect.d.ts.map +1 -0
- package/dist/migration/nativeEffect.js +598 -0
- 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 -45
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -49
- 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 +25 -266
- package/src/Array.ts +7 -39
- package/src/Chunk.ts +6 -55
- 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/DateTime.ts +473 -0
- package/src/Effect.ts +10 -32
- package/src/Emailer.ts +51 -0
- package/src/Function.ts +0 -7
- 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 +987 -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 +130 -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 +531 -0
- package/src/Model/query.ts +13 -0
- package/src/Model.ts +5 -0
- package/src/NonEmptySet.ts +6 -4
- package/src/Option.ts +3 -8
- package/src/Pure.ts +20 -17
- 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 +405 -95
- 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 +10 -5
- package/src/Store.ts +325 -0
- package/src/_ext/Array.ts +4 -2
- 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 +295 -129
- 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/migration/nativeEffect.ts +645 -0
- 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 -175
- package/src/validation.ts +2 -2
- package/src/withToast.ts +133 -0
- package/test/dateTimeLocal.test.ts +55 -0
- package/test/dist/dateTimeLocal.test.d.ts.map +1 -0
- package/test/dist/nativeEffectMigration.test.d.ts.map +1 -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/nativeEffectMigration.test.ts +77 -0
- 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/dist/_ext/date.d.ts +0 -15
- package/dist/_ext/date.d.ts.map +0 -1
- package/dist/_ext/date.js +0 -16
- package/dist/_ext/misc.d.ts +0 -14
- package/dist/_ext/misc.d.ts.map +0 -1
- package/dist/_ext/misc.js +0 -17
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
- package/src/_ext/date.ts +0 -27
- package/src/_ext/misc.ts +0 -46
- 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 } 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,162 @@ 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
|
-
/**
|
|
64
|
-
* Like the default Schema `Boolean` but with `withDefault` => false
|
|
65
|
-
*/
|
|
137
|
+
/** Like the default Schema `Boolean` but with default helpers. */
|
|
66
138
|
export const Boolean = Object.assign(S.Boolean, {
|
|
67
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Construction-only default `false`. Applied only when the field is
|
|
141
|
+
* omitted from `.make(...)` input. NOT applied during decode — cannot be
|
|
142
|
+
* used to JIT-migrate database fields. See file-level note.
|
|
143
|
+
*/
|
|
144
|
+
withConstructorDefault: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(false))),
|
|
145
|
+
/**
|
|
146
|
+
* Decode-time default `false`. **Discouraged for persisted data:** a
|
|
147
|
+
* missing field may be data corruption, not an old-shape document; silently
|
|
148
|
+
* substituting `false` hides the problem. Prefer an explicit, preferably
|
|
149
|
+
* versioned migration over a decode-time fallback. See file-level note.
|
|
150
|
+
*/
|
|
68
151
|
withDecodingDefaultType: S.Boolean.pipe(S.withDecodingDefaultType(Effect.succeed(false)))
|
|
69
152
|
})
|
|
70
153
|
|
|
71
154
|
/**
|
|
72
|
-
* You probably want to use `Finite` instead of this.
|
|
73
|
-
*
|
|
155
|
+
* You probably want to use `Finite` instead of this. Like the default Schema
|
|
156
|
+
* `Number` but with default helpers.
|
|
74
157
|
*/
|
|
75
158
|
export const Number = Object.assign(S.Number, {
|
|
76
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Construction-only default `0`. Applied only when the field is omitted
|
|
161
|
+
* from `.make(...)` input. NOT applied during decode — cannot be used to
|
|
162
|
+
* JIT-migrate database fields. See file-level note.
|
|
163
|
+
*/
|
|
164
|
+
withConstructorDefault: S.Number.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
165
|
+
/**
|
|
166
|
+
* Decode-time default `0`. **Discouraged for persisted data:** a missing
|
|
167
|
+
* field may be data corruption, not an old-shape document; silently
|
|
168
|
+
* substituting `0` hides the problem. Prefer an explicit, preferably
|
|
169
|
+
* versioned migration over a decode-time fallback. See file-level note.
|
|
170
|
+
*/
|
|
77
171
|
withDecodingDefaultType: S.Number.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
78
172
|
})
|
|
79
173
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
174
|
+
// Generation-only: native Arbitrary otherwise samples up to ±Number.MAX_VALUE, so
|
|
175
|
+
// derived totals overflow to Infinity. Validation and JSON Schema are unaffected.
|
|
176
|
+
const realisticFiniteSamples = S.makeFilter<number>(() => undefined, {
|
|
177
|
+
arbitraryConstraint: { order: Order.Number, minimum: -1_000_000, maximum: 1_000_000 }
|
|
178
|
+
})
|
|
179
|
+
const FiniteSchema = S.Finite.pipe(S.check(realisticFiniteSamples))
|
|
180
|
+
|
|
181
|
+
/** Like the default Schema `Finite` but with default helpers. */
|
|
182
|
+
export const Finite = Object.assign(FiniteSchema, {
|
|
183
|
+
/**
|
|
184
|
+
* Construction-only default `0`. Applied only when the field is omitted
|
|
185
|
+
* from `.make(...)` input. NOT applied during decode — cannot be used to
|
|
186
|
+
* JIT-migrate database fields. See file-level note.
|
|
187
|
+
*/
|
|
188
|
+
withConstructorDefault: FiniteSchema.pipe(S.withConstructorDefault(Effect.succeed(0))),
|
|
189
|
+
/**
|
|
190
|
+
* Decode-time default `0`. **Discouraged for persisted data:** a missing
|
|
191
|
+
* field may be data corruption, not an old-shape document; silently
|
|
192
|
+
* substituting `0` hides the problem. Prefer an explicit, preferably
|
|
193
|
+
* versioned migration over a decode-time fallback. See file-level note.
|
|
194
|
+
*/
|
|
195
|
+
withDecodingDefaultType: FiniteSchema.pipe(S.withDecodingDefaultType(Effect.succeed(0)))
|
|
86
196
|
})
|
|
87
197
|
|
|
88
|
-
/**
|
|
89
|
-
* Like the default Schema `Literals` but with `withDefault` => literals[0]
|
|
90
|
-
*/
|
|
198
|
+
/** Like the default Schema `Literals` but with default helpers. Default value is `literals[0]`. */
|
|
91
199
|
export const Literals = <const Literals extends NonEmptyReadonlyArray<AST.LiteralValue>>(literals: Literals) =>
|
|
92
200
|
pipe(
|
|
93
201
|
S.Literals(literals),
|
|
94
202
|
(s) =>
|
|
95
203
|
Object.assign(s, {
|
|
204
|
+
/** Override the default literal value used by `withConstructorDefault` / `withDecodingDefaultType`. */
|
|
96
205
|
changeDefault: <A extends Literals[number]>(a: A) => {
|
|
97
206
|
return Object.assign(S.Literals(literals), {
|
|
98
207
|
Default: a,
|
|
99
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Construction-only default. Applied only when the field is
|
|
210
|
+
* omitted from `.make(...)` input. NOT applied during decode —
|
|
211
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
212
|
+
* note.
|
|
213
|
+
*/
|
|
214
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(a))),
|
|
215
|
+
/**
|
|
216
|
+
* Decode-time default. **Discouraged for persisted data:** a
|
|
217
|
+
* missing field may be data corruption, not an old-shape
|
|
218
|
+
* document; silently substituting hides the problem. Prefer an
|
|
219
|
+
* explicit, preferably versioned migration over a decode-time
|
|
220
|
+
* fallback. See file-level note.
|
|
221
|
+
*/
|
|
100
222
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(a)))
|
|
101
223
|
}) // todo: copy annotations from original?
|
|
102
224
|
},
|
|
103
|
-
|
|
104
|
-
|
|
225
|
+
// 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
|
|
226
|
+
Default: literals[0] as Literals[0],
|
|
227
|
+
/**
|
|
228
|
+
* Construction-only default `literals[0]`. Applied only when the
|
|
229
|
+
* field is omitted from `.make(...)` input. NOT applied during
|
|
230
|
+
* decode — cannot be used to JIT-migrate database fields. See
|
|
231
|
+
* file-level note.
|
|
232
|
+
*/
|
|
233
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(literals[0]))),
|
|
234
|
+
/**
|
|
235
|
+
* Decode-time default `literals[0]`. **Discouraged for persisted
|
|
236
|
+
* data:** a missing field may be data corruption, not an old-shape
|
|
237
|
+
* document; silently substituting hides the problem. Prefer an
|
|
238
|
+
* explicit, preferably versioned migration over a decode-time
|
|
239
|
+
* fallback. See file-level note.
|
|
240
|
+
*/
|
|
105
241
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(literals[0])))
|
|
106
242
|
})
|
|
107
243
|
)
|
|
108
244
|
|
|
109
|
-
/**
|
|
110
|
-
* Like the default Schema `Array` but with `withDefault` => []
|
|
111
|
-
*/
|
|
245
|
+
/** Like the default Schema `Array` but with default helpers. */
|
|
112
246
|
export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
113
247
|
return pipe(
|
|
114
|
-
S.Array(value),
|
|
248
|
+
S.Array(value).annotate(concurrencyUnbounded),
|
|
115
249
|
(s) =>
|
|
116
250
|
Object.assign(s, {
|
|
117
|
-
|
|
251
|
+
/**
|
|
252
|
+
* Construction-only default `[]`. Applied only when the field is
|
|
253
|
+
* omitted from `.make(...)` input. NOT applied during decode —
|
|
254
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
255
|
+
* note.
|
|
256
|
+
*/
|
|
257
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => []))),
|
|
258
|
+
/**
|
|
259
|
+
* Decode-time default `[]`. **Discouraged for persisted data:** a
|
|
260
|
+
* missing field may be data corruption, not an old-shape document;
|
|
261
|
+
* silently substituting `[]` hides the problem. Prefer an explicit,
|
|
262
|
+
* preferably versioned migration over a decode-time fallback. See
|
|
263
|
+
* file-level note.
|
|
264
|
+
*/
|
|
118
265
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => [])))
|
|
119
266
|
})
|
|
120
267
|
)
|
|
@@ -126,13 +273,13 @@ export function Array<ValueSchema extends S.Top>(value: ValueSchema) {
|
|
|
126
273
|
export const ReadonlySetFromArray = <ValueSchema extends S.Top>(value: ValueSchema) => {
|
|
127
274
|
const from = S
|
|
128
275
|
.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<
|
|
276
|
+
.annotate({ ...concurrencyUnbounded, expected: "an array of unique items that will be decoded as a ReadonlySet" })
|
|
277
|
+
const to = S.instanceOf(Set) as S.instanceOf<ReadonlySet<ValueSchema["Type"]>>
|
|
131
278
|
const schema = from.pipe(
|
|
132
279
|
S.decodeTo(
|
|
133
280
|
to,
|
|
134
281
|
SchemaTransformation.transform({
|
|
135
|
-
decode: (arr) => new Set(arr) as ReadonlySet<
|
|
282
|
+
decode: (arr) => new Set(arr) as ReadonlySet<ValueSchema["Type"]>,
|
|
136
283
|
encode: (set) => [...set]
|
|
137
284
|
})
|
|
138
285
|
)
|
|
@@ -149,9 +296,12 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
|
|
|
149
296
|
}) => {
|
|
150
297
|
const from = S
|
|
151
298
|
.Array(S.Tuple([pair.key, pair.value]))
|
|
152
|
-
.annotate({
|
|
299
|
+
.annotate({
|
|
300
|
+
...concurrencyUnbounded,
|
|
301
|
+
expected: "an array of key-value tuples that will be decoded as a ReadonlyMap"
|
|
302
|
+
})
|
|
153
303
|
const to = S.instanceOf(Map) as S.instanceOf<
|
|
154
|
-
ReadonlyMap<
|
|
304
|
+
ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>
|
|
155
305
|
>
|
|
156
306
|
const schema = from.pipe(
|
|
157
307
|
S.decodeTo(
|
|
@@ -159,7 +309,7 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
|
|
|
159
309
|
SchemaTransformation.transform({
|
|
160
310
|
decode: (
|
|
161
311
|
arr
|
|
162
|
-
) => new Map(arr) as ReadonlyMap<
|
|
312
|
+
) => new Map(arr) as ReadonlyMap<KeySchema["Type"], ValueSchema["Type"]>,
|
|
163
313
|
encode: (
|
|
164
314
|
map
|
|
165
315
|
) => [...map.entries()] as any // fu
|
|
@@ -169,65 +319,142 @@ export const ReadonlyMapFromArray = <KeySchema extends S.Top, ValueSchema extend
|
|
|
169
319
|
return schema
|
|
170
320
|
}
|
|
171
321
|
|
|
172
|
-
/**
|
|
173
|
-
* Like the default Schema `ReadonlySet` but from Array with `withDefault` => new Set()
|
|
174
|
-
*/
|
|
322
|
+
/** Like the default Schema `ReadonlySet` (Encoded is `Set`) with default helpers. */
|
|
175
323
|
export const ReadonlySet = <ValueSchema extends S.Top>(value: ValueSchema) =>
|
|
176
324
|
pipe(
|
|
177
|
-
|
|
325
|
+
S.ReadonlySet(value),
|
|
178
326
|
(s) =>
|
|
179
327
|
Object.assign(s, {
|
|
180
|
-
|
|
328
|
+
/**
|
|
329
|
+
* Construction-only default `new Set()`. Applied only when the field
|
|
330
|
+
* is omitted from `.make(...)` input. NOT applied during decode —
|
|
331
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
332
|
+
* note.
|
|
333
|
+
*/
|
|
334
|
+
withConstructorDefault: s.pipe(
|
|
335
|
+
S.withConstructorDefault(Effect.sync(() => new Set<ValueSchema["Type"]>()))
|
|
336
|
+
),
|
|
337
|
+
/**
|
|
338
|
+
* Decode-time default `new Set()`. **Discouraged for persisted
|
|
339
|
+
* data:** a missing field may be data corruption, not an old-shape
|
|
340
|
+
* document; silently substituting an empty set hides the problem.
|
|
341
|
+
* Prefer an explicit, preferably versioned migration over a
|
|
342
|
+
* decode-time fallback. See file-level note.
|
|
343
|
+
*/
|
|
181
344
|
withDecodingDefaultType: s.pipe(
|
|
182
|
-
S.withDecodingDefaultType(Effect.sync(() => new Set<
|
|
345
|
+
S.withDecodingDefaultType(Effect.sync(() => new Set<ValueSchema["Type"]>()))
|
|
183
346
|
)
|
|
184
347
|
})
|
|
185
348
|
)
|
|
186
349
|
|
|
187
|
-
/**
|
|
188
|
-
* Like the default Schema `ReadonlyMap` but from Array with `withDefault` => new Map()
|
|
189
|
-
*/
|
|
350
|
+
/** Like the default Schema `ReadonlyMap` (Encoded is `Map`) with default helpers. */
|
|
190
351
|
export const ReadonlyMap = <KeySchema extends S.Top, ValueSchema extends S.Top>(pair: {
|
|
191
352
|
readonly key: KeySchema
|
|
192
353
|
readonly value: ValueSchema
|
|
193
354
|
}) =>
|
|
194
355
|
pipe(
|
|
195
|
-
|
|
356
|
+
S.ReadonlyMap(pair.key, pair.value),
|
|
196
357
|
(s) =>
|
|
197
358
|
Object.assign(s, {
|
|
198
|
-
|
|
359
|
+
/**
|
|
360
|
+
* Construction-only default `new Map()`. Applied only when the field
|
|
361
|
+
* is omitted from `.make(...)` input. NOT applied during decode —
|
|
362
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
363
|
+
* note.
|
|
364
|
+
*/
|
|
365
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new Map()))),
|
|
366
|
+
/**
|
|
367
|
+
* Decode-time default `new Map()`. **Discouraged for persisted
|
|
368
|
+
* data:** a missing field may be data corruption, not an old-shape
|
|
369
|
+
* document; silently substituting an empty map hides the problem.
|
|
370
|
+
* Prefer an explicit, preferably versioned migration over a
|
|
371
|
+
* decode-time fallback. See file-level note.
|
|
372
|
+
*/
|
|
199
373
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new Map())))
|
|
200
374
|
})
|
|
201
375
|
)
|
|
202
376
|
|
|
203
|
-
/**
|
|
204
|
-
* Like the default Schema `NullOr` but with `withDefault` => null
|
|
205
|
-
*/
|
|
377
|
+
/** Like the default Schema `NullOr` but with default helpers. */
|
|
206
378
|
export const NullOr = <Schema extends S.Top>(self: Schema) =>
|
|
207
379
|
pipe(
|
|
208
380
|
S.NullOr(self),
|
|
209
381
|
(s) =>
|
|
210
382
|
Object.assign(s, {
|
|
211
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Construction-only default `null`. Applied only when the field is
|
|
385
|
+
* omitted from `.make(...)` input. NOT applied during decode —
|
|
386
|
+
* cannot be used to JIT-migrate database fields. See file-level
|
|
387
|
+
* note.
|
|
388
|
+
*/
|
|
389
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.succeed(null))),
|
|
390
|
+
/**
|
|
391
|
+
* Decode-time default `null`. **Discouraged for persisted data:** a
|
|
392
|
+
* missing field may be data corruption, not an old-shape document;
|
|
393
|
+
* silently substituting `null` hides the problem. Prefer an
|
|
394
|
+
* explicit, preferably versioned migration over a decode-time
|
|
395
|
+
* fallback. See file-level note.
|
|
396
|
+
*/
|
|
212
397
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.succeed(null)))
|
|
213
398
|
})
|
|
214
399
|
)
|
|
215
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Attach a `withConstructorDefault` of `new Date()` to any schema.
|
|
403
|
+
*
|
|
404
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
405
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
406
|
+
* JIT-migrate database fields. See file-level note.
|
|
407
|
+
*/
|
|
216
408
|
export const defaultDate = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
217
409
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => new global.Date())))
|
|
218
410
|
|
|
411
|
+
/**
|
|
412
|
+
* Attach a `withConstructorDefault` of `false` to any schema.
|
|
413
|
+
*
|
|
414
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
415
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
416
|
+
* JIT-migrate database fields. See file-level note.
|
|
417
|
+
*/
|
|
219
418
|
export const defaultBool = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
220
419
|
schema.pipe(S.withConstructorDefault(Effect.succeed(false)))
|
|
221
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Attach a `withConstructorDefault` of `null` to any schema.
|
|
423
|
+
*
|
|
424
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
425
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
426
|
+
* JIT-migrate database fields. See file-level note.
|
|
427
|
+
*/
|
|
222
428
|
export const defaultNullable = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
223
429
|
schema.pipe(S.withConstructorDefault(Effect.succeed(null)))
|
|
224
430
|
|
|
431
|
+
/**
|
|
432
|
+
* Attach a `withConstructorDefault` of `[]` to any schema.
|
|
433
|
+
*
|
|
434
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
435
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
436
|
+
* JIT-migrate database fields. See file-level note.
|
|
437
|
+
*/
|
|
225
438
|
export const defaultArray = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
226
439
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => [])))
|
|
227
440
|
|
|
441
|
+
/**
|
|
442
|
+
* Attach a `withConstructorDefault` of `new Map()` to any schema.
|
|
443
|
+
*
|
|
444
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
445
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
446
|
+
* JIT-migrate database fields. See file-level note.
|
|
447
|
+
*/
|
|
228
448
|
export const defaultMap = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
229
449
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Map())))
|
|
230
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Attach a `withConstructorDefault` of `new Set()` to any schema.
|
|
453
|
+
*
|
|
454
|
+
* **Construction-only.** Applied only when the field is omitted from
|
|
455
|
+
* `.make(...)` input. NOT applied during decode — cannot be used to
|
|
456
|
+
* JIT-migrate database fields. See file-level note.
|
|
457
|
+
*/
|
|
231
458
|
export const defaultSet = <Schema extends S.Top & S.WithoutConstructorDefault>(schema: Schema) =>
|
|
232
459
|
schema.pipe(S.withConstructorDefault(Effect.sync(() => new Set())))
|
|
233
460
|
|
|
@@ -258,10 +485,23 @@ export type WithDefaults<Self extends S.Top> = (
|
|
|
258
485
|
// export type UnionToIntersection3<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I
|
|
259
486
|
// : never
|
|
260
487
|
|
|
488
|
+
/** Union of core `Schema.Date` (Date objects) and string-encoded `DateFromString`, with default helpers. */
|
|
261
489
|
export const inputDate = extendM(
|
|
262
|
-
S.Union([S.
|
|
490
|
+
S.Union([S.Date, DateFromString]),
|
|
263
491
|
(s) => ({
|
|
264
|
-
|
|
492
|
+
/**
|
|
493
|
+
* Construction-only default `new Date()`. Applied only when the field is
|
|
494
|
+
* omitted from `.make(...)` input. NOT applied during decode — cannot be
|
|
495
|
+
* used to JIT-migrate database fields. See file-level note.
|
|
496
|
+
*/
|
|
497
|
+
withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))),
|
|
498
|
+
/**
|
|
499
|
+
* Decode-time default `new Date()`. **Discouraged for persisted data:** a
|
|
500
|
+
* missing field may be data corruption, not an old-shape document;
|
|
501
|
+
* silently substituting `new Date()` hides the problem. Prefer an
|
|
502
|
+
* explicit, preferably versioned migration over a decode-time fallback.
|
|
503
|
+
* See file-level note.
|
|
504
|
+
*/
|
|
265
505
|
withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date())))
|
|
266
506
|
})
|
|
267
507
|
)
|
|
@@ -275,19 +515,87 @@ export function makeOptional<NER extends S.Struct.Fields>(
|
|
|
275
515
|
): {
|
|
276
516
|
[K in keyof NER]: NER[K] extends S.Top ? ReturnType<typeof S.optional<NER[K] & S.Top>> : any
|
|
277
517
|
} {
|
|
278
|
-
return
|
|
518
|
+
return Struct.keys(t).reduce((prev, cur) => {
|
|
279
519
|
prev[cur] = S.optional(t[cur] as any)
|
|
280
520
|
return prev
|
|
281
521
|
}, {} as any)
|
|
282
522
|
}
|
|
283
523
|
|
|
524
|
+
// `Omit` erases the polymorphic `this` of "this-returning" methods (`annotate`, `annotateKey`,
|
|
525
|
+
// `check`, `rebuild`): extracting them through a mapped type binds their `this["Rebuild"]` back to
|
|
526
|
+
// the original `T`, not to this intersection. So overriding just `Rebuild` above is not enough, those
|
|
527
|
+
// four methods have to be redeclared here with a concrete return type instead of `this["Rebuild"]`.
|
|
528
|
+
export type DropConstructorDefault<T extends S.Top> =
|
|
529
|
+
& Omit<
|
|
530
|
+
T,
|
|
531
|
+
"~type.constructor.default" | "Rebuild" | "annotate" | "annotateKey" | "check" | "rebuild"
|
|
532
|
+
>
|
|
533
|
+
& {
|
|
534
|
+
readonly "~type.constructor.default": "no-default"
|
|
535
|
+
readonly "Rebuild": DropConstructorDefault<T>
|
|
536
|
+
annotate(annotations: S.Annotations.Bottom<T["Type"], T["~type.parameters"]>): DropConstructorDefault<T>
|
|
537
|
+
annotateKey(annotations: S.Annotations.Key<T["Type"]>): DropConstructorDefault<T>
|
|
538
|
+
check(
|
|
539
|
+
...checks: readonly [SchemaAST.Check<T["Type"]>, ...Array<SchemaAST.Check<T["Type"]>>]
|
|
540
|
+
): DropConstructorDefault<T>
|
|
541
|
+
rebuild(ast: T["ast"]): DropConstructorDefault<T>
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// `replaceContext` is `@internal` in `effect/SchemaAST` (stripped from its public types, though the
|
|
545
|
+
// export itself still ships in the compiled JS). Effect's own `withConstructorDefault` goes through the
|
|
546
|
+
// same function internally, so this mirrors an existing code path rather than reaching for something
|
|
547
|
+
// novel, but it's still an internal API that could move or disappear without a signal.
|
|
548
|
+
const SchemaASTInternal = SchemaAST as unknown as {
|
|
549
|
+
readonly replaceContext: <A extends SchemaAST.AST>(ast: A, context: SchemaAST.Context | undefined) => A
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export interface DropConstructorDefaultLambda extends Struct.Lambda {
|
|
553
|
+
<T extends S.Top>(self: T): DropConstructorDefault<T>
|
|
554
|
+
readonly "~lambda.out": DropConstructorDefault<this["~lambda.in"] & S.Top>
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* A `Struct.Lambda` that removes a field's `withConstructorDefault`, if any.
|
|
559
|
+
* Compose with `Struct.pick`/`Struct.omit` via `Struct.map` to derive a
|
|
560
|
+
* partial-update schema from a "create" schema without inheriting defaults
|
|
561
|
+
* that would resurrect omitted fields in `.make(...)` output:
|
|
562
|
+
*
|
|
563
|
+
* ```ts
|
|
564
|
+
* import { flow } from "effect/Function"
|
|
565
|
+
* import * as Struct from "effect/Struct"
|
|
566
|
+
* import * as S from "effect-app/Schema"
|
|
567
|
+
*
|
|
568
|
+
* const Person = S.Struct({
|
|
569
|
+
* id: S.String,
|
|
570
|
+
* name: S.String.pipe(S.optionalKey, S.withConstructorDefault(() => ""))
|
|
571
|
+
* })
|
|
572
|
+
*
|
|
573
|
+
* const UpdatePerson = Person.mapFields(
|
|
574
|
+
* flow(Struct.omit(["id"]), Struct.map(S.dropConstructorDefault))
|
|
575
|
+
* )
|
|
576
|
+
* ```
|
|
577
|
+
*/
|
|
578
|
+
export const dropConstructorDefault: DropConstructorDefaultLambda = Struct.lambda<DropConstructorDefaultLambda>(
|
|
579
|
+
(schema) => {
|
|
580
|
+
const context = schema.ast.context
|
|
581
|
+
if (!context?.constructorDefault) return schema
|
|
582
|
+
return schema.rebuild(
|
|
583
|
+
SchemaASTInternal.replaceContext(
|
|
584
|
+
schema.ast,
|
|
585
|
+
new SchemaAST.Context(context.isOptional, context.isMutable, undefined, context.annotations)
|
|
586
|
+
)
|
|
587
|
+
)
|
|
588
|
+
}
|
|
589
|
+
)
|
|
590
|
+
|
|
284
591
|
export function makeExactOptional<NER extends S.Struct.Fields>(
|
|
285
592
|
t: NER
|
|
286
593
|
): {
|
|
287
|
-
[K in keyof NER]: NER[K] extends S.Top ? ReturnType<typeof S.optionalKey<NER[K] & S.Top
|
|
594
|
+
[K in keyof NER]: NER[K] extends S.Top ? DropConstructorDefault<ReturnType<typeof S.optionalKey<NER[K] & S.Top>>>
|
|
595
|
+
: any
|
|
288
596
|
} {
|
|
289
|
-
return
|
|
290
|
-
prev[cur] = S.optionalKey(t[cur] as any)
|
|
597
|
+
return Struct.keys(t).reduce((prev, cur) => {
|
|
598
|
+
prev[cur] = dropConstructorDefault(S.optionalKey(t[cur] as any))
|
|
291
599
|
return prev
|
|
292
600
|
}, {} as any)
|
|
293
601
|
}
|
|
@@ -304,20 +612,20 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
|
|
|
304
612
|
from.pipe(
|
|
305
613
|
S.decodeTo(
|
|
306
614
|
to,
|
|
307
|
-
SchemaTransformation.
|
|
615
|
+
SchemaTransformation.transformEffect({
|
|
308
616
|
decode: (input: any, options: any) => Effect.sync(() => decode(input, options)),
|
|
309
617
|
encode: (i: any) =>
|
|
310
618
|
Effect.fail(
|
|
311
619
|
new SchemaIssue.Forbidden(
|
|
312
|
-
|
|
313
|
-
|
|
620
|
+
{ message: "One way schema transformation, encoding is not allowed" },
|
|
621
|
+
i
|
|
314
622
|
)
|
|
315
623
|
)
|
|
316
624
|
})
|
|
317
625
|
)
|
|
318
626
|
)
|
|
319
627
|
|
|
320
|
-
/** A version of
|
|
628
|
+
/** A version of transformEffect which is only a one way mapping of From->To */
|
|
321
629
|
export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
322
630
|
from: From,
|
|
323
631
|
to: To,
|
|
@@ -329,43 +637,45 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
|
|
|
329
637
|
from.pipe(
|
|
330
638
|
S.decodeTo(
|
|
331
639
|
to,
|
|
332
|
-
SchemaTransformation.
|
|
640
|
+
SchemaTransformation.transformEffect({
|
|
333
641
|
decode,
|
|
334
642
|
encode: (i: any) =>
|
|
335
643
|
Effect.fail(
|
|
336
644
|
new SchemaIssue.Forbidden(
|
|
337
|
-
|
|
338
|
-
|
|
645
|
+
{ message: "One way schema transformation, encoding is not allowed" },
|
|
646
|
+
i
|
|
339
647
|
)
|
|
340
648
|
)
|
|
341
649
|
})
|
|
342
650
|
)
|
|
343
651
|
)
|
|
344
652
|
|
|
345
|
-
export const provide
|
|
346
|
-
self: Self,
|
|
347
|
-
context: Context.Context<R>
|
|
348
|
-
): ProvidedCodec<Self, R> => {
|
|
653
|
+
export const provide: {
|
|
654
|
+
<R>(context: Context.Context<R>): <Self extends S.Top>(self: Self) => ProvidedCodec<Self, R>
|
|
655
|
+
<Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R>
|
|
656
|
+
} = Function.dual(2, <Self extends S.Top, R>(self: Self, context: Context.Context<R>): ProvidedCodec<Self, R> => {
|
|
349
657
|
const prov = Effect.provide(context)
|
|
350
658
|
return self.pipe(
|
|
351
659
|
S.middlewareDecoding((effect) => prov(effect)),
|
|
352
660
|
S.middlewareEncoding((effect) => prov(effect))
|
|
353
|
-
)
|
|
354
|
-
}
|
|
355
|
-
export const contextFromServices =
|
|
661
|
+
)
|
|
662
|
+
})
|
|
663
|
+
export const contextFromServices = Effect.fnUntraced(function*<
|
|
664
|
+
Self extends S.Top,
|
|
665
|
+
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
666
|
+
>(self: Self, ...services: Tags) {
|
|
667
|
+
const context: Context.Context<Context.Service.Identifier<Tags[number]>> = Context.pick(...services)(
|
|
668
|
+
yield* Effect.context<Context.Service.Identifier<Tags[number]>>()
|
|
669
|
+
)
|
|
670
|
+
return provide(self, context)
|
|
671
|
+
}) as <
|
|
356
672
|
Self extends S.Top,
|
|
357
673
|
Tags extends ReadonlyArray<Context.Key<any, any>>
|
|
358
674
|
>(
|
|
359
675
|
self: Self,
|
|
360
676
|
...services: Tags
|
|
361
|
-
)
|
|
677
|
+
) => Effect.Effect<
|
|
362
678
|
ProvidedCodec<Self, Context.Service.Identifier<Tags[number]>>,
|
|
363
679
|
never,
|
|
364
680
|
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
|
-
})
|
|
681
|
+
>
|