effect-app 4.0.0-beta.98 → 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 +2030 -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/test/schema.test.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// import { generateFromArbitrary } from "@effect-app/infra/test"
|
|
2
|
-
import
|
|
2
|
+
import * as Array from "effect-app/Array"
|
|
3
|
+
import * as S from "effect-app/Schema"
|
|
3
4
|
import { specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import * as Struct from "effect/Struct"
|
|
4
7
|
import { describe, expect, expectTypeOf, test } from "vitest"
|
|
5
8
|
|
|
6
9
|
const A = S.Struct({ a: S.NonEmptyString255, email: S.NullOr(S.Email) })
|
|
@@ -17,20 +20,168 @@ test("works", () => {
|
|
|
17
20
|
test("literal default works", () => {
|
|
18
21
|
const l = S.Literals(["a", "b"])
|
|
19
22
|
expect(l.Default).toBe("a")
|
|
20
|
-
|
|
23
|
+
expectTypeOf(l.Default).toEqualTypeOf<"a">()
|
|
24
|
+
const s = S.Struct({ l: l.withConstructorDefault })
|
|
21
25
|
expect(s.make({}).l).toBe("a")
|
|
22
26
|
|
|
23
27
|
const l2 = l.changeDefault("b")
|
|
24
|
-
const s2 = S.Struct({ l: l2.
|
|
28
|
+
const s2 = S.Struct({ l: l2.withConstructorDefault })
|
|
25
29
|
expect(s2.make({}).l).toBe("b")
|
|
26
30
|
})
|
|
27
31
|
|
|
32
|
+
// Toy schema for `dropConstructorDefault`: a field with a constructor
|
|
33
|
+
// default that should NOT auto-populate when deriving a partial-update
|
|
34
|
+
// schema from a "create" schema.
|
|
35
|
+
const Person = S.Struct({
|
|
36
|
+
name: S.String,
|
|
37
|
+
notes: S.NullOr(S.String).pipe(S.optionalKey, S.withConstructorDefault(Effect.succeed(null)))
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test("plain Struct.omit leaks the inherited constructor default (the bug)", () => {
|
|
41
|
+
const UpdatePersonBuggy = S.Struct(Struct.omit(Person.fields, [] as const))
|
|
42
|
+
expect(UpdatePersonBuggy.make({ name: "Mario" })).toEqual({ name: "Mario", notes: null })
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test("Struct.map(S.dropConstructorDefault) strips the inherited constructor default (the fix)", () => {
|
|
46
|
+
const UpdatePersonFixed = S.Struct(Struct.map(Struct.omit(Person.fields, [] as const), S.dropConstructorDefault))
|
|
47
|
+
expect(UpdatePersonFixed.make({ name: "Mario" })).toEqual({ name: "Mario" })
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("Struct.map(S.dropConstructorDefault) still omits the requested keys, like Struct.omit", () => {
|
|
51
|
+
const NameOnly = S.Struct(Struct.map(Struct.omit(Person.fields, ["notes"] as const), S.dropConstructorDefault))
|
|
52
|
+
expect(NameOnly.make({ name: "Mario" })).toEqual({ name: "Mario" })
|
|
53
|
+
expectTypeOf<typeof NameOnly.Type>().toEqualTypeOf<{ readonly name: string }>()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test("makeExactOptional drops the inherited constructor default too", () => {
|
|
57
|
+
const UpdatePerson = S.Struct(S.makeExactOptional(Person.fields))
|
|
58
|
+
expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" })
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test("dropConstructorDefault stays 'no-default' after annotate/annotateKey (Rebuild fix)", () => {
|
|
62
|
+
const notesDropped = S.dropConstructorDefault(Person.fields.notes)
|
|
63
|
+
expectTypeOf<(typeof notesDropped)["~type.constructor.default"]>().toEqualTypeOf<"no-default">()
|
|
64
|
+
|
|
65
|
+
const notesAnnotated = notesDropped.annotate({ description: "no longer defaulted" })
|
|
66
|
+
expectTypeOf<(typeof notesAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">()
|
|
67
|
+
|
|
68
|
+
const notesKeyAnnotated = notesDropped.annotateKey({ description: "no longer defaulted (key)" })
|
|
69
|
+
expectTypeOf<(typeof notesKeyAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">()
|
|
70
|
+
|
|
71
|
+
const UpdatePerson = S.Struct({ name: Person.fields.name, notes: notesAnnotated })
|
|
72
|
+
expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" })
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test("NonEmptyString255.Type uses the named brand alias", () => {
|
|
76
|
+
type A = typeof S.NonEmptyString255.Type
|
|
77
|
+
type B = string & S.NonEmptyString255Brand
|
|
78
|
+
expectTypeOf<A>().toEqualTypeOf<B>()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test("Opaque accepts an explicit Encoded type", () => {
|
|
82
|
+
interface User {
|
|
83
|
+
readonly id: string
|
|
84
|
+
readonly _tag: "User"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface UserEncoded {
|
|
88
|
+
readonly id: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const baseSchema = S.Struct({ id: S.String, name: S.String })
|
|
92
|
+
const _UserSchema = S.Opaque<User, UserEncoded>()(baseSchema)
|
|
93
|
+
|
|
94
|
+
expectTypeOf<S.Codec.Encoded<typeof _UserSchema>>().toEqualTypeOf<UserEncoded>()
|
|
95
|
+
expectTypeOf<S.Schema.Type<typeof _UserSchema>>().toEqualTypeOf<User>()
|
|
96
|
+
expectTypeOf<S.Codec.Encoded<typeof _UserSchema>>().not.toEqualTypeOf<S.Codec.Encoded<typeof baseSchema>>()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test("Opaque with one generic keeps the base encoded shape", () => {
|
|
100
|
+
interface User {
|
|
101
|
+
readonly id: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const baseSchema = S.Struct({ id: S.String })
|
|
105
|
+
const _UserSchema = S.Opaque<User>()(baseSchema)
|
|
106
|
+
|
|
107
|
+
expectTypeOf<S.Codec.Encoded<typeof _UserSchema>>().toEqualTypeOf<{ readonly id: string }>()
|
|
108
|
+
expectTypeOf<S.Schema.Type<typeof _UserSchema>>().toEqualTypeOf<User>()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test("Opaque preserves optional Struct.make input", () => {
|
|
112
|
+
interface User {
|
|
113
|
+
readonly a?: string | undefined
|
|
114
|
+
readonly b?: number | undefined
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const schema = S.Opaque<User>()(S.Struct({
|
|
118
|
+
a: S.optional(S.String),
|
|
119
|
+
b: S.optional(S.Number)
|
|
120
|
+
}))
|
|
121
|
+
|
|
122
|
+
const made = schema.make()
|
|
123
|
+
expect(made).toEqual({})
|
|
124
|
+
expectTypeOf(made).toEqualTypeOf<User>()
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test("Opaque preserves optional TaggedStruct.make input", () => {
|
|
128
|
+
interface OnlyTag {
|
|
129
|
+
readonly _tag: "OnlyTag"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const schema = S.Opaque<OnlyTag>()(S.TaggedStruct("OnlyTag", {}))
|
|
133
|
+
|
|
134
|
+
const made = schema.make()
|
|
135
|
+
expect(made).toEqual({ _tag: "OnlyTag" })
|
|
136
|
+
expectTypeOf(made).toEqualTypeOf<OnlyTag>()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test("S.Literals([\"A\", \"B\"]).Default is typed as \"A\"", () => {
|
|
140
|
+
const l = S.Literals(["A", "B"])
|
|
141
|
+
expect(l.Default).toBe("A")
|
|
142
|
+
expectTypeOf(l.Default).toEqualTypeOf<"A">()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test("Struct.make accepts void when all fields are optional", () => {
|
|
146
|
+
const schema = S.Struct({
|
|
147
|
+
a: S.optional(S.String),
|
|
148
|
+
b: S.optional(S.Number)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
const made = schema.make()
|
|
152
|
+
expect(made).toEqual({})
|
|
153
|
+
expectTypeOf(made).toEqualTypeOf<{ readonly a?: string | undefined; readonly b?: number | undefined }>()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test("StructNestedEncoded resolves Struct and from.Encoded", () => {
|
|
157
|
+
const plain = S.Struct({ a: S.String, b: S.NullOr(S.String) })
|
|
158
|
+
const encodedKeys = plain.pipe(S.encodeKeys({ b: "b_encoded" }))
|
|
159
|
+
|
|
160
|
+
expectTypeOf<S.StructNestedEncoded<typeof plain>>().toEqualTypeOf<{
|
|
161
|
+
readonly a: string
|
|
162
|
+
readonly b: string | null
|
|
163
|
+
}>()
|
|
164
|
+
|
|
165
|
+
expectTypeOf<S.StructNestedEncoded<typeof encodedKeys>>().toEqualTypeOf<{
|
|
166
|
+
readonly a: string
|
|
167
|
+
readonly b_encoded: string | null
|
|
168
|
+
}>()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
test("TaggedStruct.make accepts void when only constructor-default fields exist", () => {
|
|
172
|
+
const schema = S.TaggedStruct("OnlyTag", {})
|
|
173
|
+
|
|
174
|
+
const made = schema.make()
|
|
175
|
+
expect(made).toEqual({ _tag: "OnlyTag" })
|
|
176
|
+
expectTypeOf(made).toEqualTypeOf<{ readonly _tag: "OnlyTag" }>()
|
|
177
|
+
})
|
|
178
|
+
|
|
28
179
|
test("tagged union derives tag map and tags from v4 literal ast", () => {
|
|
29
|
-
const schema = S.TaggedUnion(
|
|
180
|
+
const schema = S.TaggedUnion([
|
|
30
181
|
S.TaggedStruct("A", { a: S.String }),
|
|
31
182
|
S.TaggedStruct("B", { b: S.Finite }),
|
|
32
183
|
S.TaggedStruct("C", { c: S.Boolean })
|
|
33
|
-
)
|
|
184
|
+
])
|
|
34
185
|
const caseA = schema.cases["A"]
|
|
35
186
|
const caseB = schema.cases["B"]
|
|
36
187
|
const caseC = schema.cases["C"]
|
|
@@ -57,21 +208,21 @@ test("tagged union derives tag map and tags from v4 literal ast", () => {
|
|
|
57
208
|
})
|
|
58
209
|
|
|
59
210
|
test("TaggedUnion tags returns a Literals schema with correct literal values", () => {
|
|
60
|
-
const schema = S.TaggedUnion(
|
|
211
|
+
const schema = S.TaggedUnion([
|
|
61
212
|
S.TaggedStruct("X", { x: S.String }),
|
|
62
213
|
S.TaggedStruct("Y", { y: S.Finite })
|
|
63
|
-
)
|
|
214
|
+
])
|
|
64
215
|
|
|
65
216
|
expect(schema.tags.literals).toEqual(["X", "Y"])
|
|
66
217
|
expectTypeOf(schema.tags.literals).toMatchTypeOf<readonly ["X", "Y"]>()
|
|
67
218
|
})
|
|
68
219
|
|
|
69
220
|
test("TaggedUnion tags.pick returns a subset of the tag literals", () => {
|
|
70
|
-
const schema = S.TaggedUnion(
|
|
221
|
+
const schema = S.TaggedUnion([
|
|
71
222
|
S.TaggedStruct("A", { a: S.String }),
|
|
72
223
|
S.TaggedStruct("B", { b: S.Finite }),
|
|
73
224
|
S.TaggedStruct("C", { c: S.Boolean })
|
|
74
|
-
)
|
|
225
|
+
])
|
|
75
226
|
|
|
76
227
|
const subset = schema.tags.pick(["A", "C"])
|
|
77
228
|
expect(subset.literals).toEqual(["A", "C"])
|
|
@@ -110,24 +261,23 @@ test("ExtendTaggedUnion adds tags to an existing Union", () => {
|
|
|
110
261
|
})
|
|
111
262
|
|
|
112
263
|
test("TaggedUnion match dispatches on _tag", () => {
|
|
113
|
-
const schema = S.TaggedUnion(
|
|
264
|
+
const schema = S.TaggedUnion([
|
|
114
265
|
S.TaggedStruct("A", { a: S.String }),
|
|
115
266
|
S.TaggedStruct("B", { b: S.Finite })
|
|
116
|
-
)
|
|
117
|
-
type T = S.Schema.Type<typeof schema>
|
|
267
|
+
])
|
|
118
268
|
|
|
119
269
|
const matcher = schema.match({
|
|
120
270
|
A: (v) => `got A: ${v.a}`,
|
|
121
271
|
B: (v) => `got B: ${v.b}`
|
|
122
272
|
})
|
|
123
|
-
expect(matcher({ _tag: "A", a: "hello" }
|
|
124
|
-
expect(matcher({ _tag: "B", b: 42 }
|
|
273
|
+
expect(matcher({ _tag: "A", a: "hello" })).toBe("got A: hello")
|
|
274
|
+
expect(matcher({ _tag: "B", b: 42 })).toBe("got B: 42")
|
|
125
275
|
})
|
|
126
276
|
|
|
127
277
|
test("TaggedUnion with single member", () => {
|
|
128
|
-
const schema = S.TaggedUnion(
|
|
278
|
+
const schema = S.TaggedUnion([
|
|
129
279
|
S.TaggedStruct("Only", { val: S.String })
|
|
130
|
-
)
|
|
280
|
+
])
|
|
131
281
|
|
|
132
282
|
expect(schema.tags.literals).toEqual(["Only"])
|
|
133
283
|
expect(S.decodeSync(schema.tags)("Only")).toBe("Only")
|
|
@@ -136,11 +286,11 @@ test("TaggedUnion with single member", () => {
|
|
|
136
286
|
})
|
|
137
287
|
|
|
138
288
|
test("TaggedUnion tags type is narrowed to the exact tag literals", () => {
|
|
139
|
-
const schema = S.TaggedUnion(
|
|
289
|
+
const schema = S.TaggedUnion([
|
|
140
290
|
S.TaggedStruct("Alpha", { a: S.String }),
|
|
141
291
|
S.TaggedStruct("Beta", { b: S.Finite }),
|
|
142
292
|
S.TaggedStruct("Gamma", { c: S.Boolean })
|
|
143
|
-
)
|
|
293
|
+
])
|
|
144
294
|
|
|
145
295
|
type Tags = S.Schema.Type<typeof schema.tags>
|
|
146
296
|
expectTypeOf<Tags>().toEqualTypeOf<"Alpha" | "Beta" | "Gamma">()
|
|
@@ -154,7 +304,7 @@ test("TaggedUnion with encodeKeys renaming a non-tag key", () => {
|
|
|
154
304
|
S.encodeKeys({ lastName: "last_name" })
|
|
155
305
|
)
|
|
156
306
|
|
|
157
|
-
const schema = S.TaggedUnion(MemberA, MemberB)
|
|
307
|
+
const schema = S.TaggedUnion([MemberA, MemberB])
|
|
158
308
|
|
|
159
309
|
expect(schema.tags.literals).toEqual(["A", "B"])
|
|
160
310
|
expect(S.decodeSync(schema.tags)("A")).toBe("A")
|
|
@@ -168,8 +318,7 @@ test("TaggedUnion with encodeKeys renaming a non-tag key", () => {
|
|
|
168
318
|
expect(decoded2).toEqual({ _tag: "B", lastName: 42 })
|
|
169
319
|
|
|
170
320
|
// encode back to snake_case
|
|
171
|
-
|
|
172
|
-
const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" } as T)
|
|
321
|
+
const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" })
|
|
173
322
|
expect(encoded).toEqual({ _tag: "A", first_name: "Alice" })
|
|
174
323
|
|
|
175
324
|
// guards work on decoded values
|
|
@@ -182,7 +331,7 @@ test("TaggedUnion with TaggedClass members", () => {
|
|
|
182
331
|
class Foo extends S.TaggedClass<Foo>()("Foo", { name: S.String }) {}
|
|
183
332
|
class Bar extends S.TaggedClass<Bar>()("Bar", { count: S.Finite }) {}
|
|
184
333
|
|
|
185
|
-
const schema = S.TaggedUnion(Foo, Bar)
|
|
334
|
+
const schema = S.TaggedUnion([Foo, Bar])
|
|
186
335
|
|
|
187
336
|
expect(schema.tags.literals).toEqual(["Foo", "Bar"])
|
|
188
337
|
expect(S.decodeSync(schema.tags)("Foo")).toBe("Foo")
|
|
@@ -236,7 +385,7 @@ describe("ReadonlySetFromArray", () => {
|
|
|
236
385
|
|
|
237
386
|
describe("ReadonlyMapFromArray", () => {
|
|
238
387
|
test("decodes an array of tuples to a Map", () => {
|
|
239
|
-
const schema = S.
|
|
388
|
+
const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.Finite })
|
|
240
389
|
const decoded = S.decodeUnknownSync(schema)([["a", 1], ["b", 2]])
|
|
241
390
|
expect(decoded).toEqual(new Map([["a", 1], ["b", 2]]))
|
|
242
391
|
})
|
|
@@ -286,34 +435,48 @@ describe("ReadonlyMapFromArray", () => {
|
|
|
286
435
|
})
|
|
287
436
|
})
|
|
288
437
|
|
|
289
|
-
describe("ReadonlySet (with
|
|
290
|
-
test("make provides
|
|
438
|
+
describe("ReadonlySet (with withConstructorDefault)", () => {
|
|
439
|
+
test("make provides withConstructorDefault", () => {
|
|
291
440
|
const schema = S.ReadonlySet(S.NumberFromString)
|
|
292
|
-
const struct = S.Struct({ items: schema.
|
|
441
|
+
const struct = S.Struct({ items: schema.withConstructorDefault })
|
|
293
442
|
const made = struct.make({})
|
|
294
443
|
expect(made.items).toEqual(new Set())
|
|
295
444
|
})
|
|
296
445
|
|
|
297
|
-
test("decodes
|
|
446
|
+
test("decodes a Set with NumberFromString values", () => {
|
|
298
447
|
const schema = S.ReadonlySet(S.NumberFromString)
|
|
299
|
-
const decoded = S.decodeUnknownSync(schema)(["1", "2"])
|
|
448
|
+
const decoded = S.decodeUnknownSync(schema)(new Set(["1", "2"]))
|
|
300
449
|
expect(decoded).toEqual(new Set([1, 2]))
|
|
301
450
|
})
|
|
451
|
+
|
|
452
|
+
test("Encoded is a Set, not an array", () => {
|
|
453
|
+
const schema = S.ReadonlySet(S.String)
|
|
454
|
+
const encoded = S.encodeSync(schema)(new Set(["a"]))
|
|
455
|
+
expect(encoded).toEqual(new Set(["a"]))
|
|
456
|
+
expectTypeOf(encoded).toEqualTypeOf<ReadonlySet<string>>()
|
|
457
|
+
})
|
|
302
458
|
})
|
|
303
459
|
|
|
304
|
-
describe("ReadonlyMap (with
|
|
305
|
-
test("make provides
|
|
460
|
+
describe("ReadonlyMap (with withConstructorDefault)", () => {
|
|
461
|
+
test("make provides withConstructorDefault", () => {
|
|
306
462
|
const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String })
|
|
307
|
-
const struct = S.Struct({ items: schema.
|
|
463
|
+
const struct = S.Struct({ items: schema.withConstructorDefault })
|
|
308
464
|
const made = struct.make({})
|
|
309
465
|
expect(made.items).toEqual(new Map())
|
|
310
466
|
})
|
|
311
467
|
|
|
312
|
-
test("decodes
|
|
468
|
+
test("decodes a Map with NumberFromString keys", () => {
|
|
313
469
|
const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String })
|
|
314
|
-
const decoded = S.decodeUnknownSync(schema)([["1", "one"]])
|
|
470
|
+
const decoded = S.decodeUnknownSync(schema)(new Map([["1", "one"]]))
|
|
315
471
|
expect(decoded).toEqual(new Map([[1, "one"]]))
|
|
316
472
|
})
|
|
473
|
+
|
|
474
|
+
test("Encoded is a Map, not an array of tuples", () => {
|
|
475
|
+
const schema = S.ReadonlyMap({ key: S.String, value: S.Finite })
|
|
476
|
+
const encoded = S.encodeSync(schema)(new Map([["a", 1]]))
|
|
477
|
+
expect(encoded).toEqual(new Map([["a", 1]]))
|
|
478
|
+
expectTypeOf(encoded).toEqualTypeOf<ReadonlyMap<string, number>>()
|
|
479
|
+
})
|
|
317
480
|
})
|
|
318
481
|
|
|
319
482
|
describe("JSON Schema", () => {
|
|
@@ -328,10 +491,8 @@ describe("JSON Schema", () => {
|
|
|
328
491
|
title: "Email",
|
|
329
492
|
description: "an email according to RFC 5322",
|
|
330
493
|
format: "email",
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
{ maxLength: 998 }
|
|
334
|
-
]
|
|
494
|
+
minLength: 2,
|
|
495
|
+
maxLength: 998
|
|
335
496
|
}
|
|
336
497
|
}
|
|
337
498
|
})
|
|
@@ -348,37 +509,32 @@ describe("JSON Schema", () => {
|
|
|
348
509
|
title: "Email",
|
|
349
510
|
description: "an email according to RFC 5322",
|
|
350
511
|
format: "email",
|
|
351
|
-
minLength:
|
|
512
|
+
minLength: 2,
|
|
352
513
|
maxLength: 998
|
|
353
514
|
}
|
|
354
515
|
}
|
|
355
516
|
})
|
|
356
517
|
})
|
|
357
518
|
|
|
358
|
-
test("Date
|
|
519
|
+
test("Date Encoded is Date; JSON codec encodes ISO strings", () => {
|
|
520
|
+
const d = new Date("2024-01-01T00:00:00.000Z")
|
|
521
|
+
expect(S.decodeUnknownSync(S.Date)(d)).toBe(d)
|
|
522
|
+
expect(S.encodeSync(S.Date)(d)).toBe(d)
|
|
523
|
+
expect(S.encodeSync(S.toCodecJson(S.Date))(d)).toBe("2024-01-01T00:00:00.000Z")
|
|
359
524
|
const doc = S.toJsonSchemaDocument(S.Date)
|
|
360
|
-
expect(doc).
|
|
361
|
-
|
|
362
|
-
schema: { "$ref": "#/$defs/Date" },
|
|
363
|
-
definitions: {
|
|
364
|
-
Date: {
|
|
365
|
-
type: "string",
|
|
366
|
-
description: "a string in ISO 8601 format that will be decoded as a Date",
|
|
367
|
-
format: "date-time"
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
})
|
|
525
|
+
expect(doc.dialect).toBe("draft-2020-12")
|
|
526
|
+
expect(doc.schema).toEqual({ type: "string" })
|
|
371
527
|
})
|
|
372
528
|
|
|
373
|
-
test("
|
|
374
|
-
const doc = S.toJsonSchemaDocument(S.
|
|
529
|
+
test("DateFromString keeps string Encoded", () => {
|
|
530
|
+
const doc = S.toJsonSchemaDocument(S.DateFromString)
|
|
375
531
|
expect(doc).toStrictEqual({
|
|
376
532
|
dialect: "draft-2020-12",
|
|
377
|
-
schema: { "$ref": "#/$defs/
|
|
533
|
+
schema: { "$ref": "#/$defs/DateOrInvalid" },
|
|
378
534
|
definitions: {
|
|
379
|
-
|
|
535
|
+
DateOrInvalid: {
|
|
380
536
|
type: "string",
|
|
381
|
-
description: "
|
|
537
|
+
description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)",
|
|
382
538
|
format: "date-time"
|
|
383
539
|
}
|
|
384
540
|
}
|
|
@@ -416,3 +572,110 @@ describe("JSON Schema", () => {
|
|
|
416
572
|
})
|
|
417
573
|
})
|
|
418
574
|
})
|
|
575
|
+
|
|
576
|
+
describe("generateGuards", () => {
|
|
577
|
+
const StateSchema = S.TaggedUnion([
|
|
578
|
+
S.TaggedStruct("Active", { since: S.String }),
|
|
579
|
+
S.TaggedStruct("Inactive", { reason: S.String }),
|
|
580
|
+
S.TaggedStruct("Pending", { eta: S.Finite })
|
|
581
|
+
])
|
|
582
|
+
|
|
583
|
+
type State = S.Schema.Type<typeof StateSchema>
|
|
584
|
+
type Entity = { readonly state: State; readonly name: string }
|
|
585
|
+
|
|
586
|
+
const { isActive, isAnyOf, isInactive, isPending } = StateSchema.generateGuards("state")
|
|
587
|
+
|
|
588
|
+
test("isActive narrows to Active member", () => {
|
|
589
|
+
const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
|
|
590
|
+
expect(isActive(entity)).toBe(true)
|
|
591
|
+
if (isActive(entity)) {
|
|
592
|
+
expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>()
|
|
593
|
+
}
|
|
594
|
+
})
|
|
595
|
+
|
|
596
|
+
test("isActive returns false for non-Active", () => {
|
|
597
|
+
const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" }
|
|
598
|
+
expect(isActive(entity)).toBe(false)
|
|
599
|
+
})
|
|
600
|
+
|
|
601
|
+
test("isInactive narrows to Inactive member", () => {
|
|
602
|
+
const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" }
|
|
603
|
+
expect(isInactive(entity)).toBe(true)
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
test("isPending narrows to Pending member", () => {
|
|
607
|
+
const entity: Entity = { state: { _tag: "Pending", eta: 42 }, name: "foo" }
|
|
608
|
+
expect(isPending(entity)).toBe(true)
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
test("isAnyOf narrows to union of specified members", () => {
|
|
612
|
+
const isActiveOrPending = isAnyOf(["Active", "Pending"])
|
|
613
|
+
const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
|
|
614
|
+
const pending: Entity = { state: { _tag: "Pending", eta: 5 }, name: "bar" }
|
|
615
|
+
const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" }
|
|
616
|
+
|
|
617
|
+
expect(isActiveOrPending(active)).toBe(true)
|
|
618
|
+
expect(isActiveOrPending(pending)).toBe(true)
|
|
619
|
+
expect(isActiveOrPending(inactive)).toBe(false)
|
|
620
|
+
|
|
621
|
+
if (isActiveOrPending(active)) {
|
|
622
|
+
expectTypeOf(active.state).toEqualTypeOf<
|
|
623
|
+
{ readonly _tag: "Active"; readonly since: string } | { readonly _tag: "Pending"; readonly eta: number }
|
|
624
|
+
>()
|
|
625
|
+
}
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
test("guards use schema-based validation (built-in guards)", () => {
|
|
629
|
+
expect(StateSchema.guards.Active({ _tag: "Active" })).toBe(false)
|
|
630
|
+
expect(StateSchema.guards.Active({ _tag: "Active", since: "2024-01-01" })).toBe(true)
|
|
631
|
+
})
|
|
632
|
+
})
|
|
633
|
+
|
|
634
|
+
describe("generateGuardsFor", () => {
|
|
635
|
+
const StateSchema = S.TaggedUnion([
|
|
636
|
+
S.TaggedStruct("Active", { since: S.String }),
|
|
637
|
+
S.TaggedStruct("Inactive", { reason: S.String }),
|
|
638
|
+
S.TaggedStruct("Pending", { eta: S.Finite })
|
|
639
|
+
])
|
|
640
|
+
|
|
641
|
+
type State = S.Schema.Type<typeof StateSchema>
|
|
642
|
+
type Entity = { readonly state: State; readonly name: string }
|
|
643
|
+
|
|
644
|
+
const { isActive, isAnyOf } = StateSchema.generateGuardsFor<Entity>()("state")
|
|
645
|
+
|
|
646
|
+
test("isActive narrows to Active member", () => {
|
|
647
|
+
const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
|
|
648
|
+
expect(isActive(entity)).toBe(true)
|
|
649
|
+
if (isActive(entity)) {
|
|
650
|
+
expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>()
|
|
651
|
+
}
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
test("isActive returns false for non-Active", () => {
|
|
655
|
+
const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" }
|
|
656
|
+
expect(isActive(entity)).toBe(false)
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
test("isAnyOf narrows to union of specified members", () => {
|
|
660
|
+
const isActiveOrPending = isAnyOf(["Active", "Pending"])
|
|
661
|
+
const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" }
|
|
662
|
+
const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" }
|
|
663
|
+
|
|
664
|
+
expect(isActiveOrPending(active)).toBe(true)
|
|
665
|
+
expect(isActiveOrPending(inactive)).toBe(false)
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
test("ExtendTaggedUnion also exposes generateGuardsFor", () => {
|
|
669
|
+
const union = S.Union([
|
|
670
|
+
S.TaggedStruct("X", { x: S.String }),
|
|
671
|
+
S.TaggedStruct("Y", { y: S.Finite })
|
|
672
|
+
])
|
|
673
|
+
const extended = S.ExtendTaggedUnion(union)
|
|
674
|
+
type Obj = { readonly field: S.Schema.Type<typeof extended> }
|
|
675
|
+
const { isX, isY } = extended.generateGuardsFor<Obj>()("field")
|
|
676
|
+
|
|
677
|
+
expect(isX({ field: { _tag: "X", x: "hi" } })).toBe(true)
|
|
678
|
+
expect(isX({ field: { _tag: "Y", y: 1 } })).toBe(false)
|
|
679
|
+
expect(isY({ field: { _tag: "Y", y: 1 } })).toBe(true)
|
|
680
|
+
})
|
|
681
|
+
})
|
package/test/secretURL.test.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Chunk from "effect/Chunk"
|
|
2
|
+
import * as Config from "effect/Config"
|
|
3
|
+
import * as ConfigProvider from "effect/ConfigProvider"
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
2
5
|
import { describe, expect, test } from "vitest"
|
|
3
6
|
import { fromChunk, fromString, isSecretURL, make, secretURL, unsafeWipe, value } from "../src/Config/SecretURL.js"
|
|
4
7
|
|