effect-app 4.0.0-beta.27 → 4.0.0-beta.272
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 +1275 -0
- package/dist/Array.d.ts +2 -1
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +4 -4
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Config/SecretURL.d.ts +3 -1
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +3 -6
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- package/dist/Config/internal/configSecretURL.js +2 -2
- package/dist/Config.d.ts +2 -0
- package/dist/Config.d.ts.map +1 -0
- package/dist/Config.js +8 -0
- package/dist/ConfigProvider.d.ts +2 -0
- package/dist/ConfigProvider.d.ts.map +1 -0
- package/dist/ConfigProvider.js +6 -0
- package/dist/{ServiceMap.d.ts → Context.d.ts} +18 -20
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +67 -0
- package/dist/Effect.d.ts +10 -9
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +5 -8
- package/dist/Emailer.d.ts +51 -0
- package/dist/Emailer.d.ts.map +1 -0
- package/dist/Emailer.js +7 -0
- package/dist/Function.d.ts.map +1 -1
- package/dist/Layer.d.ts +10 -6
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +3 -2
- package/dist/Model/Repository/Registry.d.ts +22 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +18 -0
- package/dist/Model/Repository/ext.d.ts +60 -0
- package/dist/Model/Repository/ext.d.ts.map +1 -0
- package/dist/Model/Repository/ext.js +122 -0
- package/dist/Model/Repository/internal/internal.d.ts +63 -0
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
- package/dist/Model/Repository/internal/internal.js +430 -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 +54 -0
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
- package/dist/Model/Repository/makeRepo.js +27 -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 +58 -0
- package/dist/Model/Repository/validation.d.ts.map +1 -0
- package/dist/Model/Repository/validation.js +32 -0
- package/dist/Model/Repository.d.ts +7 -0
- package/dist/Model/Repository.d.ts.map +1 -0
- package/dist/Model/Repository.js +7 -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 +493 -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 +136 -0
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
- package/dist/Model/query/new-kid-interpreter.js +336 -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 +5 -0
- package/dist/Model.d.ts.map +1 -0
- package/dist/Model.js +5 -0
- package/dist/NonEmptySet.d.ts +3 -1
- package/dist/NonEmptySet.d.ts.map +1 -1
- package/dist/NonEmptySet.js +2 -2
- package/dist/Option.d.ts +1 -0
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +3 -1
- package/dist/Pure.d.ts +7 -5
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +17 -14
- package/dist/QueueMaker.d.ts +13 -0
- package/dist/QueueMaker.d.ts.map +1 -0
- package/dist/QueueMaker.js +4 -0
- package/dist/RequestContext.d.ts +154 -0
- package/dist/RequestContext.d.ts.map +1 -0
- package/dist/RequestContext.js +54 -0
- package/dist/Schema/Class.d.ts +160 -19
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +260 -17
- package/dist/Schema/FastCheck.d.ts.map +1 -1
- package/dist/Schema/SchemaParser.d.ts +5 -0
- package/dist/Schema/SchemaParser.d.ts.map +1 -0
- package/dist/Schema/SchemaParser.js +6 -0
- package/dist/Schema/SpecialJsonSchema.d.ts +34 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +118 -0
- package/dist/Schema/SpecialOpenApi.d.ts +32 -0
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
- package/dist/Schema/SpecialOpenApi.js +123 -0
- package/dist/Schema/brand.d.ts +4 -2
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +3 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +7 -4
- package/dist/Schema/ext.d.ts +338 -55
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +358 -53
- package/dist/Schema/moreStrings.d.ts +82 -36
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +49 -42
- package/dist/Schema/numbers.d.ts +34 -21
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +55 -12
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +6 -3
- 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 +213 -7
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +190 -11
- 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 +170 -0
- package/dist/Store.d.ts.map +1 -0
- package/dist/Store.js +121 -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/client/InvalidationKeys.d.ts +29 -0
- package/dist/client/InvalidationKeys.d.ts.map +1 -0
- package/dist/client/InvalidationKeys.js +33 -0
- package/dist/client/apiClientFactory.d.ts +19 -31
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +104 -34
- package/dist/client/clientFor.d.ts +52 -18
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +82 -27
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +75 -19
- package/dist/client/makeClient.d.ts +494 -32
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +66 -24
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/faker.d.ts.map +1 -1
- package/dist/http/Request.d.ts +1 -1
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +2 -2
- package/dist/ids.d.ts +42 -14
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +30 -5
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/middleware.d.ts +13 -7
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +14 -8
- package/dist/rpc/Invalidation.d.ts +420 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +168 -0
- package/dist/rpc/MiddlewareMaker.d.ts +11 -7
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +59 -38
- package/dist/rpc/RpcContextMap.d.ts +3 -3
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +14 -10
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/rpc.d.ts +1 -1
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -2
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +40 -0
- package/dist/setupRequest.d.ts +19 -0
- package/dist/setupRequest.d.ts.map +1 -0
- package/dist/setupRequest.js +69 -0
- package/dist/toast.d.ts +51 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +34 -0
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +4 -5
- package/dist/utils/effectify.d.ts +1 -1
- package/dist/utils/effectify.d.ts.map +1 -1
- package/dist/utils/effectify.js +2 -2
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/gen.d.ts +4 -4
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logLevel.d.ts +2 -2
- package/dist/utils/logLevel.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +4 -3
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +4 -4
- package/dist/utils.d.ts +34 -39
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -37
- package/dist/validation/validators.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/withToast.d.ts +30 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +64 -0
- package/package.json +22 -247
- package/src/Array.ts +5 -5
- package/src/Chunk.ts +3 -3
- package/src/Config/SecretURL.ts +6 -3
- package/src/Config/internal/configSecretURL.ts +2 -2
- package/src/Config.ts +7 -0
- package/src/ConfigProvider.ts +5 -0
- package/src/{ServiceMap.ts → Context.ts} +56 -63
- package/src/Effect.ts +14 -16
- package/src/Emailer.ts +51 -0
- package/src/Inputify.type.ts +1 -1
- package/src/Layer.ts +11 -7
- package/src/Model/Repository/Registry.ts +35 -0
- package/src/Model/Repository/ext.ts +375 -0
- package/src/Model/Repository/internal/internal.ts +741 -0
- package/src/Model/Repository/legacy.ts +29 -0
- package/src/Model/Repository/makeRepo.ts +145 -0
- package/src/Model/Repository/service.ts +676 -0
- package/src/Model/Repository/validation.ts +31 -0
- package/src/Model/Repository.ts +6 -0
- package/src/Model/dsl.ts +129 -0
- package/src/Model/filter/filterApi.ts +60 -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 +2694 -0
- package/src/Model/query/new-kid-interpreter.ts +484 -0
- package/src/Model/query.ts +13 -0
- package/src/Model.ts +4 -0
- package/src/NonEmptySet.ts +6 -4
- package/src/Option.ts +2 -0
- package/src/Pure.ts +22 -20
- package/src/QueueMaker.ts +19 -0
- package/src/RequestContext.ts +95 -0
- package/src/Schema/Class.ts +593 -59
- package/src/Schema/SchemaParser.ts +12 -0
- package/src/Schema/SpecialJsonSchema.ts +139 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +22 -2
- package/src/Schema/email.ts +9 -4
- package/src/Schema/ext.ts +446 -91
- package/src/Schema/moreStrings.ts +147 -68
- package/src/Schema/numbers.ts +97 -28
- package/src/Schema/phoneNumber.ts +9 -5
- package/src/Schema/strings.ts +23 -14
- package/src/Schema.ts +389 -25
- package/src/Set.ts +6 -2
- package/src/Store.ts +277 -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/client/InvalidationKeys.ts +50 -0
- package/src/client/apiClientFactory.ts +234 -135
- package/src/client/clientFor.ts +105 -34
- package/src/client/errors.ts +100 -29
- package/src/client/makeClient.ts +594 -73
- package/src/client.ts +5 -4
- package/src/http/Request.ts +3 -3
- package/src/http.ts +1 -1
- package/src/ids.ts +33 -6
- package/src/index.ts +20 -23
- package/src/middleware.ts +13 -9
- package/src/rpc/Invalidation.ts +261 -0
- package/src/rpc/MiddlewareMaker.ts +88 -80
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +7 -6
- package/src/rpc/RpcMiddleware.ts +19 -13
- package/src/rpc.ts +4 -4
- package/src/runtime.ts +56 -0
- package/src/setupRequest.ts +134 -0
- package/src/toast.ts +54 -0
- package/src/transform.ts +4 -4
- package/src/utils/effectify.ts +1 -1
- package/src/utils/gen.ts +8 -8
- package/src/utils/logLevel.ts +1 -1
- package/src/utils/logger.ts +4 -3
- package/src/utils.ts +85 -158
- package/src/validation.ts +2 -2
- package/src/withToast.ts +133 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/secretURL.test.d.ts.map +1 -0
- package/test/dist/special.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +1 -1
- package/test/rpc.test.ts +46 -6
- package/test/schema.test.ts +459 -30
- package/test/secretURL.test.ts +160 -0
- package/test/special.test.ts +1258 -0
- package/test/utils.test.ts +7 -7
- package/tsconfig.base.json +6 -5
- package/tsconfig.json +3 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +29 -29
- package/tsconfig.test.json +2 -2
- package/dist/Operations.d.ts +0 -123
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -29
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
package/src/Schema/Class.ts
CHANGED
|
@@ -1,95 +1,629 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import type * as Cause from "effect/Cause"
|
|
3
|
+
import * as Effect from "effect/Effect"
|
|
4
|
+
import * as Option from "effect/Option"
|
|
4
5
|
import * as S from "effect/Schema"
|
|
6
|
+
import * as SchemaAST from "effect/SchemaAST"
|
|
7
|
+
import * as SchemaGetter from "effect/SchemaGetter"
|
|
8
|
+
import * as SchemaIssue from "effect/SchemaIssue"
|
|
9
|
+
import * as SchemaTransformation from "effect/SchemaTransformation"
|
|
10
|
+
import { copyOrigin } from "../utils.ts"
|
|
11
|
+
import { concurrencyUnbounded } from "./ext.ts"
|
|
12
|
+
import * as SchemaParser from "./SchemaParser.ts"
|
|
5
13
|
|
|
6
14
|
type ClassAnnotations<Self> = S.Annotations.Declaration<Self, readonly [any]>
|
|
7
15
|
|
|
8
|
-
export interface EnhancedClass<Self, SchemaS extends S.Top & { readonly fields: Struct.Fields }, Inherited>
|
|
9
|
-
extends S.Class<Self, SchemaS, Inherited
|
|
16
|
+
export interface EnhancedClass<Self, SchemaS extends S.Top & { readonly fields: S.Struct.Fields }, Inherited>
|
|
17
|
+
extends S.Class<Self, SchemaS, Inherited>
|
|
10
18
|
{
|
|
19
|
+
/**
|
|
20
|
+
* See `copyOrigin` docs in `utils.ts` for return-type design details.
|
|
21
|
+
*/
|
|
22
|
+
readonly copy: ReturnType<typeof copyOrigin<new(_: any) => Self>>
|
|
11
23
|
}
|
|
12
24
|
type MissingSelfGeneric<Usage extends string, Params extends string = ""> =
|
|
13
25
|
`Missing \`Self\` generic - use \`class Self extends ${Usage}<Self>()(${Params}{ ... })\``
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
// include: <NewProps extends S.Struct.Fields>(
|
|
17
|
-
// fnc: (fields: Fields) => NewProps
|
|
18
|
-
// ) => NewProps
|
|
19
|
-
pick: <P extends keyof Fields>(...keys: readonly P[]) => Pick<Fields, P>
|
|
20
|
-
omit: <P extends keyof Fields>(...keys: readonly P[]) => Omit<Fields, P>
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type HasFields<Fields extends Struct.Fields> = {
|
|
27
|
+
type HasFields<Fields extends S.Struct.Fields> = {
|
|
24
28
|
readonly fields: Fields
|
|
25
29
|
} | {
|
|
26
30
|
readonly from: HasFields<Fields>
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
type ClassOptions = {
|
|
34
|
+
readonly strict?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export declare const ExtendedSchemaNoEncoded: unique symbol
|
|
38
|
+
|
|
39
|
+
export type ExtendedSchemaNoEncoded = typeof ExtendedSchemaNoEncoded
|
|
40
|
+
|
|
41
|
+
type WithEncoded<SchemaS extends S.Top, Encoded> = Omit<SchemaS, "Encoded"> & { readonly Encoded: Encoded }
|
|
42
|
+
|
|
43
|
+
type ExtendedSchema<SchemaS extends S.Top, Encoded> = [Encoded] extends [ExtendedSchemaNoEncoded] ? SchemaS
|
|
44
|
+
: WithEncoded<SchemaS, Encoded>
|
|
45
|
+
|
|
46
|
+
export type Class<Self, S extends S.Top & { readonly fields: S.Struct.Fields }, Inherited> = EnhancedClass<
|
|
47
|
+
Self,
|
|
48
|
+
S,
|
|
49
|
+
Inherited
|
|
50
|
+
>
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Build a modified Declaration that accepts struct-matching values during
|
|
54
|
+
* encoding, given the original Declaration and the class's fields.
|
|
55
|
+
*/
|
|
56
|
+
function makeRelaxedDeclaration(
|
|
57
|
+
ast: SchemaAST.Declaration,
|
|
58
|
+
fields: S.Struct.Fields,
|
|
59
|
+
cls: any
|
|
60
|
+
): SchemaAST.Declaration {
|
|
61
|
+
const parseOptions = ast.annotations?.["parseOptions"] as SchemaAST.ParseOptions | undefined
|
|
62
|
+
const structSchema = S.Struct(fields)
|
|
63
|
+
const annotatedStruct = parseOptions ? S.toType(structSchema).annotate({ parseOptions }) : S.toType(structSchema)
|
|
64
|
+
const decodeStruct = SchemaParser.decodeUnknownEffect(annotatedStruct)
|
|
65
|
+
|
|
66
|
+
return new SchemaAST.Declaration(
|
|
67
|
+
ast.typeParameters,
|
|
68
|
+
() => (input: unknown, self: SchemaAST.Declaration, options: SchemaAST.ParseOptions) => {
|
|
69
|
+
if (input instanceof cls) {
|
|
70
|
+
return Effect.succeed(input)
|
|
71
|
+
}
|
|
72
|
+
if (input !== null && typeof input === "object") {
|
|
73
|
+
return decodeStruct(input, options)
|
|
74
|
+
}
|
|
75
|
+
return Effect.fail(new SchemaIssue.InvalidType(self, Option.some(input)))
|
|
76
|
+
},
|
|
77
|
+
ast.annotations,
|
|
78
|
+
ast.checks,
|
|
79
|
+
ast.encoding,
|
|
80
|
+
ast.context
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Class — like Schema.Class but with relaxed encoding
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Like `Schema.Class`, but the resulting class accepts plain objects matching
|
|
90
|
+
* the struct schema during encoding — not only `instanceof` or type-id
|
|
91
|
+
* checks.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* import * as Schema from "effect/Schema"
|
|
96
|
+
* import { Class } from "./Class.ts"
|
|
97
|
+
*
|
|
98
|
+
* class A extends Class<A>("A")({ a: Schema.String }) {}
|
|
99
|
+
*
|
|
100
|
+
* // Construction works as normal:
|
|
101
|
+
* new A({ a: "hello" })
|
|
102
|
+
*
|
|
103
|
+
* // Encoding accepts plain objects:
|
|
104
|
+
* Schema.encodeUnknownSync(A)({ a: "hello" }) // { a: "hello" }
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export const Class: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand = {}>(
|
|
108
|
+
identifier: string
|
|
109
|
+
) => <Fields extends S.Struct.Fields>(
|
|
30
110
|
fieldsOr: Fields | HasFields<Fields>,
|
|
31
|
-
annotations?: ClassAnnotations<Self
|
|
111
|
+
annotations?: ClassAnnotations<Self>,
|
|
112
|
+
options?: ClassOptions
|
|
32
113
|
) => [Self] extends [never] ? MissingSelfGeneric<"Class">
|
|
33
114
|
: EnhancedClass<
|
|
34
115
|
Self,
|
|
35
|
-
S.Struct<Fields>,
|
|
36
|
-
|
|
37
|
-
> = (identifier) => (fields, annotations) => {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
116
|
+
ExtendedSchema<S.Struct<Fields>, Encoded>,
|
|
117
|
+
Brand
|
|
118
|
+
> = (identifier) => (fields, annotations, options) => {
|
|
119
|
+
const relaxed = options?.strict === false
|
|
120
|
+
// Build the original Schema.Class
|
|
121
|
+
const Base = (S.Class as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations })
|
|
122
|
+
// Get the original ast getter from the base class
|
|
123
|
+
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!
|
|
124
|
+
|
|
125
|
+
// Cache per-class to avoid recomputing
|
|
126
|
+
const astCache = new WeakMap<any, SchemaAST.Declaration>()
|
|
127
|
+
const copyCache = new WeakMap<any, ReturnType<typeof copyOrigin>>()
|
|
128
|
+
|
|
129
|
+
return class extends Base {
|
|
130
|
+
static get copy() {
|
|
131
|
+
let cached = copyCache.get(this)
|
|
132
|
+
if (cached === undefined) {
|
|
133
|
+
cached = copyOrigin(this)
|
|
134
|
+
copyCache.set(this, cached)
|
|
135
|
+
}
|
|
136
|
+
return cached
|
|
137
|
+
}
|
|
138
|
+
static get ast(): SchemaAST.Declaration {
|
|
139
|
+
let cached = astCache.get(this)
|
|
140
|
+
if (cached !== undefined) return cached
|
|
141
|
+
// Call the original getter with `this` bound to the actual user class,
|
|
142
|
+
// so getClassSchema(this) creates a schema that uses `new this(...)`.
|
|
143
|
+
const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration
|
|
144
|
+
cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst
|
|
145
|
+
astCache.set(this, cached)
|
|
146
|
+
return cached
|
|
147
|
+
}
|
|
148
|
+
static mapFields(f: any, options?: any) {
|
|
149
|
+
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
|
|
150
|
+
}
|
|
43
151
|
} as any
|
|
44
152
|
}
|
|
45
153
|
|
|
46
|
-
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// TaggedClass — like Schema.TaggedClass but with relaxed encoding
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Like `Schema.TaggedClass`, but the resulting class accepts plain objects
|
|
160
|
+
* matching the struct schema during encoding.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* import * as Schema from "effect/Schema"
|
|
165
|
+
* import { TaggedClass } from "./Class.ts"
|
|
166
|
+
*
|
|
167
|
+
* class Circle extends TaggedClass<Circle>()("Circle", {
|
|
168
|
+
* radius: Schema.Number
|
|
169
|
+
* }) {}
|
|
170
|
+
*
|
|
171
|
+
* Schema.encodeUnknownSync(Circle)({ _tag: "Circle", radius: 5 })
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export const TaggedClass: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand = {}>(
|
|
175
|
+
identifier?: string
|
|
176
|
+
) => <Tag extends string, Fields extends S.Struct.Fields>(
|
|
47
177
|
tag: Tag,
|
|
48
178
|
fieldsOr: Fields | HasFields<Fields>,
|
|
49
|
-
annotations?: ClassAnnotations<Self
|
|
50
|
-
|
|
179
|
+
annotations?: ClassAnnotations<Self>,
|
|
180
|
+
options?: ClassOptions
|
|
181
|
+
) => [Self] extends [never] ? MissingSelfGeneric<"TaggedClass">
|
|
51
182
|
: EnhancedClass<
|
|
52
183
|
Self,
|
|
53
|
-
S.Struct<{ readonly _tag: S.tag<Tag> } & Fields>,
|
|
54
|
-
|
|
55
|
-
> = (identifier) => (tag, fields, annotations) => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
184
|
+
ExtendedSchema<S.Struct<{ readonly _tag: S.tag<Tag> } & Fields>, Encoded>,
|
|
185
|
+
Brand
|
|
186
|
+
> = (identifier) => (tag, fields, annotations, options) => {
|
|
187
|
+
const relaxed = options?.strict === false
|
|
188
|
+
const Base = (S.TaggedClass as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations })
|
|
189
|
+
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!
|
|
190
|
+
const astCache = new WeakMap<any, SchemaAST.Declaration>()
|
|
191
|
+
const copyCache = new WeakMap<any, ReturnType<typeof copyOrigin>>()
|
|
192
|
+
|
|
193
|
+
return class extends Base {
|
|
194
|
+
static get copy() {
|
|
195
|
+
let cached = copyCache.get(this)
|
|
196
|
+
if (cached === undefined) {
|
|
197
|
+
cached = copyOrigin(this)
|
|
198
|
+
copyCache.set(this, cached)
|
|
199
|
+
}
|
|
200
|
+
return cached
|
|
201
|
+
}
|
|
202
|
+
static get ast(): SchemaAST.Declaration {
|
|
203
|
+
let cached = astCache.get(this)
|
|
204
|
+
if (cached !== undefined) return cached
|
|
205
|
+
const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration
|
|
206
|
+
cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst
|
|
207
|
+
astCache.set(this, cached)
|
|
208
|
+
return cached
|
|
209
|
+
}
|
|
210
|
+
static mapFields(f: any, options?: any) {
|
|
211
|
+
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
|
|
212
|
+
}
|
|
61
213
|
} as any
|
|
62
214
|
}
|
|
63
215
|
|
|
64
|
-
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// ErrorClass — like Schema.ErrorClass but with relaxed encoding
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
export const ErrorClass: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand = {}>(
|
|
221
|
+
identifier: string
|
|
222
|
+
) => <Fields extends S.Struct.Fields>(
|
|
65
223
|
fieldsOr: Fields | HasFields<Fields>,
|
|
66
|
-
annotations?: ClassAnnotations<Self
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
224
|
+
annotations?: ClassAnnotations<Self>,
|
|
225
|
+
options?: ClassOptions
|
|
226
|
+
) => [Self] extends [never] ? MissingSelfGeneric<"ErrorClass">
|
|
227
|
+
: EnhancedClass<
|
|
228
|
+
Self,
|
|
229
|
+
ExtendedSchema<S.Struct<Fields>, Encoded>,
|
|
230
|
+
Cause.YieldableError & Brand
|
|
231
|
+
> = (identifier) => (fields, annotations, options) => {
|
|
232
|
+
const relaxed = options?.strict === false
|
|
233
|
+
const Base = (S.ErrorClass as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations })
|
|
234
|
+
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!
|
|
235
|
+
const astCache = new WeakMap<any, SchemaAST.Declaration>()
|
|
236
|
+
const copyCache = new WeakMap<any, ReturnType<typeof copyOrigin>>()
|
|
237
|
+
|
|
238
|
+
return class extends Base {
|
|
239
|
+
static get copy() {
|
|
240
|
+
let cached = copyCache.get(this)
|
|
241
|
+
if (cached === undefined) {
|
|
242
|
+
cached = copyOrigin(this)
|
|
243
|
+
copyCache.set(this, cached)
|
|
244
|
+
}
|
|
245
|
+
return cached
|
|
246
|
+
}
|
|
247
|
+
static get ast(): SchemaAST.Declaration {
|
|
248
|
+
let cached = astCache.get(this)
|
|
249
|
+
if (cached !== undefined) return cached
|
|
250
|
+
const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration
|
|
251
|
+
cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst
|
|
252
|
+
astCache.set(this, cached)
|
|
253
|
+
return cached
|
|
254
|
+
}
|
|
255
|
+
static mapFields(f: any, options?: any) {
|
|
256
|
+
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
|
|
257
|
+
}
|
|
258
|
+
} as any
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// TaggedErrorClass — like Schema.TaggedErrorClass but with relaxed encoding
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
83
264
|
|
|
84
|
-
export const
|
|
265
|
+
export const TaggedErrorClass: <Self = never, Encoded = ExtendedSchemaNoEncoded, Brand = {}>(
|
|
85
266
|
identifier?: string
|
|
86
267
|
) => <Tag extends string, Fields extends S.Struct.Fields>(
|
|
87
268
|
tag: Tag,
|
|
88
269
|
fieldsOr: Fields | HasFields<Fields>,
|
|
89
|
-
annotations?: ClassAnnotations<Self
|
|
90
|
-
|
|
270
|
+
annotations?: ClassAnnotations<Self>,
|
|
271
|
+
options?: ClassOptions
|
|
272
|
+
) => [Self] extends [never] ? MissingSelfGeneric<"TaggedErrorClass">
|
|
273
|
+
: EnhancedClass<
|
|
274
|
+
Self,
|
|
275
|
+
ExtendedSchema<S.Struct<{ readonly _tag: S.tag<Tag> } & Fields>, Encoded>,
|
|
276
|
+
Cause.YieldableError & Brand
|
|
277
|
+
> = (identifier) => (tag, fields, annotations, options) => {
|
|
278
|
+
const relaxed = options?.strict === false
|
|
279
|
+
const Base = (S.TaggedErrorClass as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations })
|
|
280
|
+
const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")!
|
|
281
|
+
const astCache = new WeakMap<any, SchemaAST.Declaration>()
|
|
282
|
+
const copyCache = new WeakMap<any, ReturnType<typeof copyOrigin>>()
|
|
283
|
+
|
|
284
|
+
return class extends Base {
|
|
285
|
+
static get copy() {
|
|
286
|
+
let cached = copyCache.get(this)
|
|
287
|
+
if (cached === undefined) {
|
|
288
|
+
cached = copyOrigin(this)
|
|
289
|
+
copyCache.set(this, cached)
|
|
290
|
+
}
|
|
291
|
+
return cached
|
|
292
|
+
}
|
|
293
|
+
static get ast(): SchemaAST.Declaration {
|
|
294
|
+
let cached = astCache.get(this)
|
|
295
|
+
if (cached !== undefined) return cached
|
|
296
|
+
const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration
|
|
297
|
+
cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst
|
|
298
|
+
astCache.set(this, cached)
|
|
299
|
+
return cached
|
|
300
|
+
}
|
|
301
|
+
static mapFields(f: any, options?: any) {
|
|
302
|
+
return Base.mapFields(f, options).annotate(concurrencyUnbounded)
|
|
303
|
+
}
|
|
304
|
+
} as any
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface Opaque<Self, Encoded, SchemaS extends S.Top, Brand>
|
|
308
|
+
extends S.Opaque<Self, ExtendedSchema<SchemaS, Encoded>, Brand>
|
|
309
|
+
{}
|
|
310
|
+
|
|
311
|
+
export const Opaque: <Self, Encoded = ExtendedSchemaNoEncoded, Brand = {}>() => <S extends S.Top>(
|
|
312
|
+
schema: S
|
|
313
|
+
) => Opaque<Self, Encoded, S, Brand> & Omit<S, keyof S.Top> = S.Opaque as any
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Like {@link Opaque}, but the class **instance type is exactly `Self`** (the supplied
|
|
317
|
+
* decoded `Type`) instead of the structurally-computed `struct["Type"] & Brand`.
|
|
318
|
+
*
|
|
319
|
+
* Stock `Opaque` types the instance as `struct["Type"] & Brand` and only overrides the
|
|
320
|
+
* schema's `Type`/`Encoded` *members* with `Self`/`Encoded`. So `make`/`copy`/consumers
|
|
321
|
+
* still resolve the struct's mapped `Type`. With a codegen-supplied pre-expanded literal
|
|
322
|
+
* `Type` interface, `OpaqueType` lets all of those resolve a single **named** interface
|
|
323
|
+
* (resolved once per checker) instead of re-deriving the mapped `Type` — cutting
|
|
324
|
+
* instantiation on `Type`-touching consumers.
|
|
325
|
+
*
|
|
326
|
+
* Use with `class X extends OpaqueType<X.Type, X.Encoded>()(struct) {}` where `X.Type`
|
|
327
|
+
* and `X.Encoded` are generated literal interfaces (see `@effect-app/eslint-codegen-model`,
|
|
328
|
+
* `static` + `type` mode).
|
|
329
|
+
*
|
|
330
|
+
* KNOWN GAP — **no branding**: the instance type is a plain structural `Self`, so opaque
|
|
331
|
+
* types of identical shape are mutually assignable. (Stock `Opaque` is also structural by
|
|
332
|
+
* default — `Brand` defaults to `{}` — so this only differs if you passed a non-default
|
|
333
|
+
* `Brand`.) Re-introducing a nominal brand on top of a supplied `Self` (e.g. branding the
|
|
334
|
+
* generated `Type` interface) is not yet implemented.
|
|
335
|
+
*
|
|
336
|
+
* NOTE: only `Type` (via `Self`) and `Encoded` are supplied statically here; `make`'s input
|
|
337
|
+
* (`~type.make.in`) and other derived members are still computed from the struct. See
|
|
338
|
+
* a future `OpaqueShape`-style helper if those also need to be supplied.
|
|
339
|
+
*/
|
|
340
|
+
export interface OpaqueType<Self, Encoded, SchemaS extends S.Top, Brand>
|
|
341
|
+
extends S.Opaque<Self, ExtendedSchema<SchemaS, Encoded>, Brand>
|
|
342
|
+
{
|
|
343
|
+
new(_: never): Self
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export const OpaqueType: <Self, Encoded = ExtendedSchemaNoEncoded, Brand = {}>() => <S extends S.Top>(
|
|
347
|
+
schema: S
|
|
348
|
+
) => OpaqueType<Self, Encoded, S, Brand> & Omit<S, keyof S.Top> = S.Opaque as any
|
|
349
|
+
|
|
350
|
+
// Override both the `Encoded` and make-input (`~type.make.in`) members in one go,
|
|
351
|
+
// like `ExtendedSchema` does for `Encoded` alone.
|
|
352
|
+
type ExtendedShape<SchemaS extends S.Top, Encoded, MakeIn> =
|
|
353
|
+
& Omit<SchemaS, "Encoded" | "~type.make.in">
|
|
354
|
+
& { readonly Encoded: Encoded; readonly "~type.make.in": MakeIn }
|
|
355
|
+
|
|
356
|
+
type OpaqueFacadeConstructorArgs<MakeIn> = {} extends MakeIn ? [props?: MakeIn, options?: S.MakeOptions]
|
|
357
|
+
: [props: MakeIn, options?: S.MakeOptions]
|
|
358
|
+
|
|
359
|
+
// Only the codec channels are required. `copy`/`fields`/`mapFields` are NOT
|
|
360
|
+
// required here: transformed schemas (`.pipe(S.encodeKeys/annotate/filter/...)`)
|
|
361
|
+
// don't expose them at the static level, and requiring them would reject those
|
|
362
|
+
// models from facading. When present they still flow through via
|
|
363
|
+
// `OpaqueFacadeStatics`; when absent the facade simply doesn't offer them.
|
|
364
|
+
type OpaqueFacadeInput<DecodingServices, EncodingServices> = S.Top & {
|
|
365
|
+
readonly DecodingServices: DecodingServices
|
|
366
|
+
readonly EncodingServices: EncodingServices
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
type OpaqueClassFacadeInput<DecodingServices, EncodingServices> =
|
|
370
|
+
& OpaqueFacadeInput<
|
|
371
|
+
DecodingServices,
|
|
372
|
+
EncodingServices
|
|
373
|
+
>
|
|
374
|
+
& (abstract new(...args: Array<never>) => unknown)
|
|
375
|
+
|
|
376
|
+
type PrototypeFunction = Function & { prototype: object }
|
|
377
|
+
|
|
378
|
+
const ClassAnnotationId = "~effect/Schema/Class"
|
|
379
|
+
|
|
380
|
+
const getOwnOrInheritedPropertyDescriptor = (value: object, property: PropertyKey): PropertyDescriptor | undefined => {
|
|
381
|
+
let current: object | null = value
|
|
382
|
+
while (current !== null) {
|
|
383
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, property)
|
|
384
|
+
if (descriptor !== undefined) return descriptor
|
|
385
|
+
current = Object.getPrototypeOf(current)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const isClassSchemaConstructor = (value: unknown): value is PrototypeFunction => {
|
|
390
|
+
if (typeof value !== "function") return false
|
|
391
|
+
const ast = (value as { readonly ast?: unknown }).ast
|
|
392
|
+
return SchemaAST.isAST(ast) && SchemaAST.isDeclaration(ast) && ast.annotations?.[ClassAnnotationId] !== undefined
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const getFacadeClassSchema = (schema: OpaqueFacadeInput<any, any>): PrototypeFunction | undefined => {
|
|
396
|
+
if (isClassSchemaConstructor(schema)) return schema
|
|
397
|
+
const target = "to" in schema ? schema.to : undefined
|
|
398
|
+
return isClassSchemaConstructor(target) ? target : undefined
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const makePublicClassTransformation = (Public: PrototypeFunction) =>
|
|
402
|
+
new SchemaTransformation.Transformation<any, any, never, never>(
|
|
403
|
+
SchemaGetter.transform((input) => Reflect.construct(Public, [input], Public)),
|
|
404
|
+
SchemaGetter.passthrough()
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
const makeFacadeClassAst = (
|
|
408
|
+
schema: OpaqueFacadeInput<any, any>,
|
|
409
|
+
originalAst: SchemaAST.AST,
|
|
410
|
+
Public: PrototypeFunction
|
|
411
|
+
): SchemaAST.AST => {
|
|
412
|
+
if (SchemaAST.isDeclaration(schema.ast)) {
|
|
413
|
+
const schemaEncoding = schema.ast.encoding ?? []
|
|
414
|
+
if (schemaEncoding.length > 0) {
|
|
415
|
+
return new SchemaAST.Declaration(
|
|
416
|
+
schema.ast.typeParameters,
|
|
417
|
+
schema.ast.run,
|
|
418
|
+
schema.ast.annotations,
|
|
419
|
+
schema.ast.checks,
|
|
420
|
+
[new SchemaAST.Link(schemaEncoding[0]!.to, makePublicClassTransformation(Public)), ...schemaEncoding.slice(1)],
|
|
421
|
+
schema.ast.context,
|
|
422
|
+
schema.ast.encodingChecks
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return schema.rebuild(originalAst).ast
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Carry the schema's own statics, dropping the generic `S.Top` machinery and
|
|
431
|
+
// `prototype`. EXCEPT `to`: models compose each other via `X.to.fields` /
|
|
432
|
+
// `X.to.copy` at definition time, so it must survive on the facade.
|
|
433
|
+
type OpaqueFacadeStatics<SchemaS extends S.Top> = Omit<SchemaS, Exclude<keyof S.Top, "to"> | "prototype">
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Like {@link OpaqueType}, but ALSO supplies a static **make-input** shape, so
|
|
437
|
+
* `make`/`copy` resolve a named `MakeIn` interface instead of re-deriving the struct's
|
|
438
|
+
* mapped `~type.make.in`. Use with codegen-supplied `X.Type` / `X.Encoded` / `X.Make`:
|
|
439
|
+
*
|
|
440
|
+
* ```ts
|
|
441
|
+
* class X extends OpaqueShape<X.Type, X.Encoded, X.Make>()(struct) {}
|
|
442
|
+
* ```
|
|
443
|
+
*
|
|
444
|
+
* `decode`/`encode` already use the supplied `Type`/`Encoded` (they read the schema's
|
|
445
|
+
* `Type`/`Encoded` members, which are `Self`/`Encoded` here) — no separate override needed.
|
|
446
|
+
*
|
|
447
|
+
* NOTE — measured gain is modest (~5%): the struct passed to the wrapper is still
|
|
448
|
+
* constructed in full (definition cost dominates); the static shapes only cheapen
|
|
449
|
+
* consumer-side reads of `Type`/`Encoded`/`MakeIn`. Same `no-branding` gap as
|
|
450
|
+
* {@link OpaqueType}.
|
|
451
|
+
*/
|
|
452
|
+
export interface OpaqueShape<Self, Encoded, MakeIn, SchemaS extends S.Top, Brand>
|
|
453
|
+
extends S.Opaque<Self, ExtendedShape<SchemaS, Encoded, MakeIn>, Brand>
|
|
454
|
+
{
|
|
455
|
+
new(_: never): Self
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export const OpaqueShape: <Self, Encoded, MakeIn, Brand = {}>() => <S extends S.Top>(
|
|
459
|
+
schema: S
|
|
460
|
+
) => OpaqueShape<Self, Encoded, MakeIn, S, Brand> & Omit<S, keyof S.Top> = S.Opaque as any
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Shallow public view for generated model facades.
|
|
464
|
+
*
|
|
465
|
+
* The runtime value can still be the full private schema class, but emitted
|
|
466
|
+
* declarations expose only named `Type` / `Encoded` / `Make` interfaces and a
|
|
467
|
+
* small static surface. This keeps downstream project references from pulling
|
|
468
|
+
* the private struct field map back through `typeof Model`.
|
|
469
|
+
*/
|
|
470
|
+
export interface OpaqueFacade<
|
|
471
|
+
Self,
|
|
472
|
+
Encoded,
|
|
473
|
+
MakeIn,
|
|
474
|
+
DecodingServices = never,
|
|
475
|
+
EncodingServices = DecodingServices,
|
|
476
|
+
Brand = {}
|
|
477
|
+
> extends
|
|
478
|
+
S.Bottom<
|
|
479
|
+
Self,
|
|
480
|
+
Encoded,
|
|
481
|
+
DecodingServices,
|
|
482
|
+
EncodingServices,
|
|
483
|
+
SchemaAST.AST,
|
|
484
|
+
S.Codec<Self, Encoded, DecodingServices, EncodingServices>,
|
|
485
|
+
MakeIn,
|
|
486
|
+
Self,
|
|
487
|
+
readonly [],
|
|
488
|
+
MakeIn
|
|
489
|
+
>
|
|
490
|
+
{
|
|
491
|
+
new(_: never): Brand
|
|
492
|
+
readonly copy: ReturnType<typeof copyOrigin<new(_: MakeIn) => Self>>
|
|
493
|
+
// NOTE: `fields` / `mapFields` are intentionally NOT redeclared here. They are
|
|
494
|
+
// carried (precise) from the underlying schema via `OpaqueFacadeStatics`. A wide
|
|
495
|
+
// `mapFields(f: (fields: S.Struct.Fields) => To)` override would win overload
|
|
496
|
+
// resolution and erase field precision in `Q.project(X.mapFields(...))`.
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export interface OpaqueClassFacade<
|
|
91
500
|
Self,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
501
|
+
Encoded,
|
|
502
|
+
MakeIn,
|
|
503
|
+
DecodingServices = never,
|
|
504
|
+
EncodingServices = DecodingServices,
|
|
505
|
+
Brand = {}
|
|
506
|
+
> extends
|
|
507
|
+
S.Bottom<
|
|
508
|
+
Self,
|
|
509
|
+
Encoded,
|
|
510
|
+
DecodingServices,
|
|
511
|
+
EncodingServices,
|
|
512
|
+
SchemaAST.AST,
|
|
513
|
+
S.Codec<Self, Encoded, DecodingServices, EncodingServices>,
|
|
514
|
+
MakeIn,
|
|
515
|
+
Self,
|
|
516
|
+
readonly [],
|
|
517
|
+
MakeIn
|
|
518
|
+
>
|
|
519
|
+
{
|
|
520
|
+
new(...args: OpaqueFacadeConstructorArgs<MakeIn>): Brand
|
|
521
|
+
readonly copy: ReturnType<typeof copyOrigin<new(_: MakeIn) => Self>>
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export function OpaqueFacade<
|
|
525
|
+
Self,
|
|
526
|
+
Encoded,
|
|
527
|
+
MakeIn,
|
|
528
|
+
DecodingServices = never,
|
|
529
|
+
EncodingServices = DecodingServices,
|
|
530
|
+
Brand = {}
|
|
531
|
+
>() {
|
|
532
|
+
function facade<SchemaS extends OpaqueClassFacadeInput<DecodingServices, EncodingServices>>(
|
|
533
|
+
schema: SchemaS
|
|
534
|
+
):
|
|
535
|
+
& OpaqueClassFacade<Self, Encoded, MakeIn, DecodingServices, EncodingServices, Brand>
|
|
536
|
+
& OpaqueFacadeStatics<SchemaS>
|
|
537
|
+
function facade<SchemaS extends OpaqueFacadeInput<DecodingServices, EncodingServices>>(
|
|
538
|
+
schema: SchemaS
|
|
539
|
+
):
|
|
540
|
+
& OpaqueFacade<Self, Encoded, MakeIn, DecodingServices, EncodingServices, Brand>
|
|
541
|
+
& OpaqueFacadeStatics<SchemaS>
|
|
542
|
+
function facade(schema: OpaqueFacadeInput<DecodingServices, EncodingServices>) {
|
|
543
|
+
const Base = getFacadeClassSchema(schema)
|
|
544
|
+
if (Base !== undefined) {
|
|
545
|
+
const astCache = new WeakMap<object, SchemaAST.AST>()
|
|
546
|
+
const originalAstDescriptor = getOwnOrInheritedPropertyDescriptor(Base, "ast")
|
|
547
|
+
|
|
548
|
+
return class extends (Base as any) {
|
|
549
|
+
static get ast(): SchemaAST.AST {
|
|
550
|
+
let cached = astCache.get(this)
|
|
551
|
+
if (cached !== undefined) return cached
|
|
552
|
+
|
|
553
|
+
const originalAst = originalAstDescriptor?.get?.call(this) as SchemaAST.AST
|
|
554
|
+
cached = makeFacadeClassAst(schema, originalAst, this)
|
|
555
|
+
astCache.set(this, cached)
|
|
556
|
+
return cached
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
class Facade {}
|
|
562
|
+
return Object.setPrototypeOf(Facade, schema)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return facade
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Like class-schema {@link OpaqueFacade}, but for error models (`TaggedErrorClass` /
|
|
570
|
+
* `ErrorClass`). The decoded instance type carries `Cause.YieldableError`, so
|
|
571
|
+
* `yield* new MyError(...)`, `Effect.fail(myError)`, and `instanceof` all keep
|
|
572
|
+
* working through the facade — the runtime `_X` is the real error class (the
|
|
573
|
+
* facade `X extends ...(_X)` inherits its prototype), and the type reflects it.
|
|
574
|
+
* Nothing is lost vs the underlying error class.
|
|
575
|
+
*/
|
|
576
|
+
export interface OpaqueErrorFacadeClass<
|
|
577
|
+
Self,
|
|
578
|
+
Encoded,
|
|
579
|
+
MakeIn,
|
|
580
|
+
DecodingServices = never,
|
|
581
|
+
EncodingServices = DecodingServices,
|
|
582
|
+
Brand = {}
|
|
583
|
+
> extends
|
|
584
|
+
S.Bottom<
|
|
585
|
+
Self,
|
|
586
|
+
Encoded,
|
|
587
|
+
DecodingServices,
|
|
588
|
+
EncodingServices,
|
|
589
|
+
SchemaAST.AST,
|
|
590
|
+
S.Codec<Self, Encoded, DecodingServices, EncodingServices>,
|
|
591
|
+
MakeIn,
|
|
592
|
+
Self,
|
|
593
|
+
readonly [],
|
|
594
|
+
MakeIn
|
|
595
|
+
>
|
|
596
|
+
{
|
|
597
|
+
// YieldableError (not Self) on the constructed instance — like class-schema OpaqueFacade's
|
|
598
|
+
// `new(): Brand`, the data type comes from the declaration-merged `interface X`,
|
|
599
|
+
// so `Self` must NOT appear here (would recurse). Merging `X & YieldableError`
|
|
600
|
+
// makes `yield* new X()` / `Effect.fail` / `instanceof` work without losing data.
|
|
601
|
+
new(...args: OpaqueFacadeConstructorArgs<MakeIn>): Cause.YieldableError & Brand
|
|
602
|
+
readonly copy: ReturnType<typeof copyOrigin<new(_: MakeIn) => Self>>
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function OpaqueErrorFacadeClass<
|
|
606
|
+
Self,
|
|
607
|
+
Encoded,
|
|
608
|
+
MakeIn,
|
|
609
|
+
DecodingServices = never,
|
|
610
|
+
EncodingServices = DecodingServices,
|
|
611
|
+
Brand = {}
|
|
612
|
+
>() {
|
|
613
|
+
return <SchemaS extends OpaqueClassFacadeInput<DecodingServices, EncodingServices>>(
|
|
614
|
+
schema: SchemaS
|
|
615
|
+
):
|
|
616
|
+
& OpaqueErrorFacadeClass<Self, Encoded, MakeIn, DecodingServices, EncodingServices, Brand>
|
|
617
|
+
& OpaqueFacadeStatics<SchemaS> =>
|
|
618
|
+
{
|
|
619
|
+
type FacadeSchema =
|
|
620
|
+
& OpaqueErrorFacadeClass<Self, Encoded, MakeIn, DecodingServices, EncodingServices, Brand>
|
|
621
|
+
& OpaqueFacadeStatics<SchemaS>
|
|
622
|
+
|
|
623
|
+
if (typeof schema === "function") {
|
|
624
|
+
return schema as SchemaS & FacadeSchema
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
throw new TypeError("OpaqueErrorFacadeClass requires a class schema")
|
|
628
|
+
}
|
|
629
|
+
}
|