effect-app 4.0.0-beta.22 → 4.0.0-beta.24
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 +17 -0
- package/dist/Operations.d.ts +11 -11
- package/dist/Schema.d.ts +4 -7
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +33 -7
- package/package.json +1 -1
- package/src/Schema.ts +38 -11
- package/test/schema.test.ts +147 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @effect-app/prelude
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.24
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 32f71bf: Fix `Schema.TaggedUnion(...).tags` extraction for class-based members (for example `TaggedClass`) by using a local AST sentinel walker instead of relying on internal effect APIs.
|
|
8
|
+
|
|
9
|
+
Add tests covering:
|
|
10
|
+
|
|
11
|
+
- `TaggedUnion` with `encodeKeys`-wrapped members
|
|
12
|
+
- `TaggedUnion` with `TaggedClass` members
|
|
13
|
+
|
|
14
|
+
## 4.0.0-beta.23
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- ea08a79: remove `fields` constraint from TaggedUnion/tags helpers, extract `_tag` via AST walk instead
|
|
19
|
+
|
|
3
20
|
## 4.0.0-beta.22
|
|
4
21
|
|
|
5
22
|
## 4.0.0-beta.21
|
package/dist/Operations.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as S from "./Schema.js";
|
|
2
2
|
export type OperationId = S.StringId;
|
|
3
|
-
export declare const OperationId: S.WithDefaults<S.brand<S.String, "NonEmptyString" | "
|
|
3
|
+
export declare const OperationId: S.WithDefaults<S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & {
|
|
4
4
|
make: () => S.StringId;
|
|
5
|
-
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "
|
|
6
|
-
}> & S.brand<S.String, "NonEmptyString" | "
|
|
5
|
+
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & S.WithoutConstructorDefault>;
|
|
6
|
+
}> & S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & {
|
|
7
7
|
make: () => S.StringId;
|
|
8
|
-
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "
|
|
8
|
+
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & S.WithoutConstructorDefault>;
|
|
9
9
|
};
|
|
10
10
|
declare const OperationProgress_base: S.EnhancedClass<OperationProgress, S.Struct<{
|
|
11
11
|
completed: S.WithDefaults<S.brand<import("effect/Schema").Int, "NonNegativeInt" | "Int" | "NonNegativeNumber">> & S.brand<import("effect/Schema").Int, "NonNegativeInt" | "Int" | "NonNegativeNumber"> & {
|
|
@@ -20,14 +20,14 @@ export declare class OperationProgress extends OperationProgress_base {
|
|
|
20
20
|
declare const OperationSuccess_base: S.EnhancedTaggedClass<OperationSuccess, "OperationSuccess", {
|
|
21
21
|
readonly _tag: S.tag<"OperationSuccess">;
|
|
22
22
|
} & {
|
|
23
|
-
message: S.withConstructorDefault<import("effect/Schema").NullOr<S.WithDefaults<S.brand<S.String, "NonEmptyString" | "
|
|
23
|
+
message: S.withConstructorDefault<import("effect/Schema").NullOr<S.WithDefaults<S.brand<S.String, "NonEmptyString" | "NonEmptyString2k" | "NonEmptyString64k">> & S.brand<S.String, "NonEmptyString" | "NonEmptyString2k" | "NonEmptyString64k">> & S.WithoutConstructorDefault>;
|
|
24
24
|
}, OperationSuccess.Encoded>;
|
|
25
25
|
export declare class OperationSuccess extends OperationSuccess_base {
|
|
26
26
|
}
|
|
27
27
|
declare const OperationFailure_base: S.EnhancedTaggedClass<OperationFailure, "OperationFailure", {
|
|
28
28
|
readonly _tag: S.tag<"OperationFailure">;
|
|
29
29
|
} & {
|
|
30
|
-
message: S.withConstructorDefault<import("effect/Schema").NullOr<S.WithDefaults<S.brand<S.String, "NonEmptyString" | "
|
|
30
|
+
message: S.withConstructorDefault<import("effect/Schema").NullOr<S.WithDefaults<S.brand<S.String, "NonEmptyString" | "NonEmptyString2k" | "NonEmptyString64k">> & S.brand<S.String, "NonEmptyString" | "NonEmptyString2k" | "NonEmptyString64k">> & S.WithoutConstructorDefault>;
|
|
31
31
|
}, OperationFailure.Encoded>;
|
|
32
32
|
export declare class OperationFailure extends OperationFailure_base {
|
|
33
33
|
}
|
|
@@ -60,14 +60,14 @@ export declare const OperationResult: S.Union<[typeof OperationSuccess, typeof O
|
|
|
60
60
|
};
|
|
61
61
|
export type OperationResult = S.Schema.Type<typeof OperationResult>;
|
|
62
62
|
declare const Operation_base: S.EnhancedClass<Operation, S.Struct<{
|
|
63
|
-
id: S.WithDefaults<S.brand<S.String, "NonEmptyString" | "
|
|
63
|
+
id: S.WithDefaults<S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & {
|
|
64
64
|
make: () => S.StringId;
|
|
65
|
-
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "
|
|
66
|
-
}> & S.brand<S.String, "NonEmptyString" | "
|
|
65
|
+
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & S.WithoutConstructorDefault>;
|
|
66
|
+
}> & S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & {
|
|
67
67
|
make: () => S.StringId;
|
|
68
|
-
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "
|
|
68
|
+
withDefault: S.withConstructorDefault<S.brand<S.String, "NonEmptyString" | "StringId" | "NonEmptyString50" | "NonEmptyString64" | "NonEmptyString80" | "NonEmptyString100" | "NonEmptyString255" | "NonEmptyString2k" | "NonEmptyString64k"> & S.WithoutConstructorDefault>;
|
|
69
69
|
};
|
|
70
|
-
title: S.WithDefaults<S.brand<S.String, "NonEmptyString" | "
|
|
70
|
+
title: S.WithDefaults<S.brand<S.String, "NonEmptyString" | "NonEmptyString2k" | "NonEmptyString64k">> & S.brand<S.String, "NonEmptyString" | "NonEmptyString2k" | "NonEmptyString64k">;
|
|
71
71
|
progress: S.optional<typeof OperationProgress>;
|
|
72
72
|
result: S.optional<S.Union<[typeof OperationSuccess, typeof OperationFailure]> & {
|
|
73
73
|
readonly cases: {
|
package/dist/Schema.d.ts
CHANGED
|
@@ -31,20 +31,17 @@ export type Email = EmailType;
|
|
|
31
31
|
export declare const PhoneNumber: import("./Schema/ext.js").WithDefaults<import("./Schema/ext.js").WithDefaults<S.refine<PhoneNumberT, S.String>> & S.refine<PhoneNumberT, S.String>> & import("./Schema/ext.js").WithDefaults<S.refine<PhoneNumberT, S.String>> & S.refine<PhoneNumberT, S.String>;
|
|
32
32
|
export type PhoneNumber = PhoneNumberType;
|
|
33
33
|
export declare const tags: <Members extends NonEmptyReadonlyArray<(S.Top & {
|
|
34
|
-
|
|
35
|
-
_tag:
|
|
34
|
+
readonly Type: {
|
|
35
|
+
readonly _tag: string;
|
|
36
36
|
};
|
|
37
|
-
})>>(self: Members) => S.Literals<{ [Index in keyof Members]:
|
|
37
|
+
})>>(self: Members) => S.Literals<{ [Index in keyof Members]: Members[Index]["Type"]["_tag"]; }>;
|
|
38
38
|
type TaggedUnionMembers = NonEmptyReadonlyArray<S.Top & {
|
|
39
39
|
readonly Type: {
|
|
40
40
|
readonly _tag: string;
|
|
41
41
|
};
|
|
42
|
-
fields: {
|
|
43
|
-
_tag: S.tag<string>;
|
|
44
|
-
};
|
|
45
42
|
}>;
|
|
46
43
|
type TaggedUnionTags<Members extends TaggedUnionMembers> = S.Literals<{
|
|
47
|
-
[Index in keyof Members]:
|
|
44
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"];
|
|
48
45
|
}>;
|
|
49
46
|
type TaggedUnionWithTags<Members extends TaggedUnionMembers> = S.toTaggedUnion<"_tag", Members> & {
|
|
50
47
|
readonly tags: TaggedUnionTags<Members>;
|
package/dist/Schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../src/Schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,KAAK,CAAC,MAAM,eAAe,CAAA;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE5E,OAAO,EAAE,WAAW,IAAI,YAAY,EAAE,KAAK,WAAW,IAAI,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAG1G,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,gBAAgB,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA;AAE/D,cAAc,mBAAmB,CAAA;AACjC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9G,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,OAAO,KAAK,WAAW,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAA;AAEnD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,eAAe,CAAA;AAE7C,eAAO,MAAM,MAAM,eAAW,CAAA;AAC9B,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAA;AAElC,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAA;CACvB;AAKD,eAAO,MAAM,KAAK,iGAOf,CAAA;AAEH,MAAM,MAAM,KAAK,GAAG,SAAS,CAAA;AAE7B,eAAO,MAAM,WAAW,mQAQrB,CAAA;AAEH,MAAM,MAAM,WAAW,GAAG,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../src/Schema.ts"],"names":[],"mappings":"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"}
|
package/dist/Schema.js
CHANGED
|
@@ -38,14 +38,40 @@ export const PhoneNumber = PhoneNumberT
|
|
|
38
38
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
39
39
|
fakerArb((faker) => faker.phone.number)(fc).map(makePhoneNumber)
|
|
40
40
|
}), withDefaultMake);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
// Copied from SchemaAST.collectSentinels (marked @internal in effect).
|
|
42
|
+
// Returns all { key, literal } pairs that can discriminate a union member.
|
|
43
|
+
const getTagFromAST = (schema) => {
|
|
44
|
+
const sentinels = collectSentinelsFromAST(schema.ast);
|
|
45
|
+
const sentinel = sentinels.find((s) => s.key === "_tag");
|
|
46
|
+
if (sentinel !== undefined && typeof sentinel.literal === "string")
|
|
47
|
+
return sentinel.literal;
|
|
48
|
+
throw new Error("No _tag literal found on schema member");
|
|
46
49
|
};
|
|
47
|
-
|
|
50
|
+
function collectSentinelsFromAST(ast) {
|
|
51
|
+
switch (ast._tag) {
|
|
52
|
+
case "Declaration": {
|
|
53
|
+
const s = ast.annotations?.["~sentinels"];
|
|
54
|
+
return Array.isArray(s) ? s : [];
|
|
55
|
+
}
|
|
56
|
+
case "Objects":
|
|
57
|
+
return ast.propertySignatures.flatMap((ps) => {
|
|
58
|
+
const type = ps.type;
|
|
59
|
+
if (!SchemaAST.isOptional(type)) {
|
|
60
|
+
if (SchemaAST.isLiteral(type))
|
|
61
|
+
return [{ key: ps.name, literal: type.literal }];
|
|
62
|
+
if (SchemaAST.isUniqueSymbol(type))
|
|
63
|
+
return [{ key: ps.name, literal: type.symbol }];
|
|
64
|
+
}
|
|
65
|
+
return [];
|
|
66
|
+
});
|
|
67
|
+
case "Suspend":
|
|
68
|
+
return collectSentinelsFromAST(ast.thunk());
|
|
69
|
+
default:
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export const tags = (self) => S.Literals(self.map(getTagFromAST));
|
|
48
74
|
const extendTaggedUnionWithTags = (schema) => extendM(schema.pipe(S.toTaggedUnion("_tag")), () => ({ tags: tags(schema.members) }));
|
|
49
75
|
export const ExtendTaggedUnion = (schema) => extendTaggedUnionWithTags(schema);
|
|
50
76
|
export const TaggedUnion = (...a) => extendTaggedUnionWithTags(S.Union(a));
|
|
51
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
77
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2NoZW1hLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1NjaGVtYS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFlLE1BQU0sUUFBUSxDQUFBO0FBQy9DLE9BQU8sS0FBSyxDQUFDLE1BQU0sZUFBZSxDQUFBO0FBRWxDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDckMsT0FBTyxFQUFFLEtBQUssSUFBSSxNQUFNLEVBQTJCLE1BQU0sbUJBQW1CLENBQUE7QUFDNUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQ2pELE9BQU8sRUFBRSxXQUFXLElBQUksWUFBWSxFQUF1QyxNQUFNLHlCQUF5QixDQUFBO0FBQzFHLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFcEMsY0FBYyxlQUFlLENBQUE7QUFDN0IsOENBQThDO0FBQzlDLE9BQU8sRUFBRSxnQkFBZ0IsSUFBSSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFFL0QsY0FBYyxtQkFBbUIsQ0FBQTtBQUNqQyxPQUFPLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBRXRELE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDdEQsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsV0FBVyxFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDOUcsT0FBTyxFQUFFLEdBQUcsRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUV6RCxjQUFjLG1CQUFtQixDQUFBO0FBQ2pDLGNBQWMsaUJBQWlCLENBQUE7QUFDL0IsY0FBYyx5QkFBeUIsQ0FBQTtBQUN2QyxjQUFjLHFCQUFxQixDQUFBO0FBQ25DLGNBQWMseUJBQXlCLENBQUE7QUFDdkMsY0FBYyxvQkFBb0IsQ0FBQTtBQUNsQyxjQUFjLHFCQUFxQixDQUFBO0FBQ25DLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUVwRCxPQUFPLEtBQUssV0FBVyxNQUFNLG9CQUFvQixDQUFBO0FBQ2pELE9BQU8sS0FBSyxZQUFZLE1BQU0scUJBQXFCLENBQUE7QUFFbkQsT0FBTyxFQUFFLElBQUksSUFBSSxLQUFLLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFFN0MsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLE1BQU0sRUFBRSxDQUFBO0FBTzlCLE1BQU0sU0FBUyxHQUFHLENBQUMsQ0FBQyxVQUFVLENBQUMsTUFBYSxDQUFpQyxDQUFBO0FBQzdFLE1BQU0sZUFBZSxHQUFHLENBQUMsQ0FBQyxVQUFVLENBQUMsWUFBbUIsQ0FBdUMsQ0FBQTtBQUUvRixNQUFNLENBQUMsTUFBTSxLQUFLLEdBQUcsTUFBTTtLQUN4QixJQUFJLENBQ0gsQ0FBQyxDQUFDLFFBQVEsQ0FBQztJQUNULDZEQUE2RDtJQUM3RCxXQUFXLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDO0NBQy9GLENBQUMsRUFDRixlQUFlLENBQ2hCLENBQUE7QUFJSCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsWUFBWTtLQUNwQyxJQUFJLENBQ0gsQ0FBQyxDQUFDLFFBQVEsQ0FBQztJQUNULFdBQVcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFO0lBQ3hCLDZEQUE2RDtJQUM3RCxRQUFRLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQztDQUNuRSxDQUFDLEVBQ0YsZUFBZSxDQUNoQixDQUFBO0FBSUgsdUVBQXVFO0FBQ3ZFLDJFQUEyRTtBQUMzRSxNQUFNLGFBQWEsR0FBRyxDQUFDLE1BQWEsRUFBVSxFQUFFO0lBQzlDLE1BQU0sU0FBUyxHQUFHLHVCQUF1QixDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUNyRCxNQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxLQUFLLE1BQU0sQ0FBQyxDQUFBO0lBQ3hELElBQUksUUFBUSxLQUFLLFNBQVMsSUFBSSxPQUFPLFFBQVEsQ0FBQyxPQUFPLEtBQUssUUFBUTtRQUFFLE9BQU8sUUFBUSxDQUFDLE9BQU8sQ0FBQTtJQUMzRixNQUFNLElBQUksS0FBSyxDQUFDLHdDQUF3QyxDQUFDLENBQUE7QUFDM0QsQ0FBQyxDQUFBO0FBRUQsU0FBUyx1QkFBdUIsQ0FDOUIsR0FBa0I7SUFFbEIsUUFBUSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDakIsS0FBSyxhQUFhLENBQUMsQ0FBQyxDQUFDO1lBQ25CLE1BQU0sQ0FBQyxHQUFHLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxZQUFZLENBQUMsQ0FBQTtZQUN6QyxPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFBO1FBQ2xDLENBQUM7UUFDRCxLQUFLLFNBQVM7WUFDWixPQUFPLEdBQUcsQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLENBQ25DLENBQUMsRUFBRSxFQUF5RSxFQUFFO2dCQUM1RSxNQUFNLElBQUksR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFBO2dCQUNwQixJQUFJLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO29CQUNoQyxJQUFJLFNBQVMsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDO3dCQUFFLE9BQU8sQ0FBQyxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtvQkFDL0UsSUFBSSxTQUFTLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQzt3QkFBRSxPQUFPLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUE7Z0JBQ3JGLENBQUM7Z0JBQ0QsT0FBTyxFQUFFLENBQUE7WUFDWCxDQUFDLENBQ0YsQ0FBQTtRQUNILEtBQUssU0FBUztZQUNaLE9BQU8sdUJBQXVCLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUE7UUFDN0M7WUFDRSxPQUFPLEVBQUUsQ0FBQTtJQUNiLENBQUM7QUFDSCxDQUFDO0FBRUQsTUFBTSxDQUFDLE1BQU0sSUFBSSxHQUFHLENBR2xCLElBQWEsRUFDYixFQUFFLENBQ0YsQ0FBQyxDQUFDLFFBQVEsQ0FDUixJQUFJLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FFckIsQ0FLRixDQUFBO0FBZ0JILE1BQU0seUJBQXlCLEdBQUcsQ0FDaEMsTUFBd0IsRUFDTSxFQUFFLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQTtBQUV4SCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxDQUMvQixNQUF3QixFQUNNLEVBQUUsQ0FBQyx5QkFBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQTtBQUVwRSxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsQ0FFekIsR0FBRyxDQUFVLEVBQWdDLEVBQUUsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUEifQ==
|
package/package.json
CHANGED
package/src/Schema.ts
CHANGED
|
@@ -65,36 +65,63 @@ export const PhoneNumber = PhoneNumberT
|
|
|
65
65
|
|
|
66
66
|
export type PhoneNumber = PhoneNumberType
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
// Copied from SchemaAST.collectSentinels (marked @internal in effect).
|
|
69
|
+
// Returns all { key, literal } pairs that can discriminate a union member.
|
|
70
|
+
const getTagFromAST = (schema: S.Top): string => {
|
|
71
|
+
const sentinels = collectSentinelsFromAST(schema.ast)
|
|
72
|
+
const sentinel = sentinels.find((s) => s.key === "_tag")
|
|
73
|
+
if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal
|
|
74
|
+
throw new Error("No _tag literal found on schema member")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function collectSentinelsFromAST(
|
|
78
|
+
ast: SchemaAST.AST
|
|
79
|
+
): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> {
|
|
80
|
+
switch (ast._tag) {
|
|
81
|
+
case "Declaration": {
|
|
82
|
+
const s = ast.annotations?.["~sentinels"]
|
|
83
|
+
return Array.isArray(s) ? s : []
|
|
84
|
+
}
|
|
85
|
+
case "Objects":
|
|
86
|
+
return ast.propertySignatures.flatMap(
|
|
87
|
+
(ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => {
|
|
88
|
+
const type = ps.type
|
|
89
|
+
if (!SchemaAST.isOptional(type)) {
|
|
90
|
+
if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }]
|
|
91
|
+
if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }]
|
|
92
|
+
}
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
case "Suspend":
|
|
97
|
+
return collectSentinelsFromAST(ast.thunk())
|
|
98
|
+
default:
|
|
99
|
+
return []
|
|
71
100
|
}
|
|
72
|
-
return schema.ast.literal as Tag
|
|
73
101
|
}
|
|
74
102
|
|
|
75
103
|
export const tags = <
|
|
76
|
-
|
|
77
|
-
Members extends NonEmptyReadonlyArray<(S.Top & { fields: { _tag: S.tag<string> } })>
|
|
104
|
+
Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })>
|
|
78
105
|
>(
|
|
79
106
|
self: Members
|
|
80
107
|
) =>
|
|
81
108
|
S.Literals(
|
|
82
|
-
self.map(
|
|
83
|
-
[Index in keyof Members]:
|
|
109
|
+
self.map(getTagFromAST) as {
|
|
110
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
84
111
|
}
|
|
85
112
|
) as S.Literals<
|
|
86
113
|
{
|
|
87
|
-
[Index in keyof Members]:
|
|
114
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
88
115
|
}
|
|
89
116
|
>
|
|
90
117
|
|
|
91
118
|
type TaggedUnionMembers = NonEmptyReadonlyArray<
|
|
92
|
-
S.Top & { readonly Type: { readonly _tag: string }
|
|
119
|
+
S.Top & { readonly Type: { readonly _tag: string } }
|
|
93
120
|
>
|
|
94
121
|
|
|
95
122
|
type TaggedUnionTags<Members extends TaggedUnionMembers> = S.Literals<
|
|
96
123
|
{
|
|
97
|
-
[Index in keyof Members]:
|
|
124
|
+
[Index in keyof Members]: Members[Index]["Type"]["_tag"]
|
|
98
125
|
}
|
|
99
126
|
>
|
|
100
127
|
|
package/test/schema.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// import { generateFromArbitrary } from "@effect-app/infra/test"
|
|
2
2
|
import { Array, S } from "effect-app"
|
|
3
|
-
import { expect, test } from "vitest"
|
|
3
|
+
import { expect, expectTypeOf, test } from "vitest"
|
|
4
4
|
|
|
5
5
|
const A = S.Struct({ a: S.NonEmptyString255, email: S.NullOr(S.Email) })
|
|
6
6
|
test("works", () => {
|
|
@@ -54,3 +54,149 @@ test("tagged union derives tag map and tags from v4 literal ast", () => {
|
|
|
54
54
|
expect(isAOrB({ _tag: "B", b: 1 })).toBe(true)
|
|
55
55
|
expect(isAOrB({ _tag: "C", c: true })).toBe(false)
|
|
56
56
|
})
|
|
57
|
+
|
|
58
|
+
test("TaggedUnion tags returns a Literals schema with correct literal values", () => {
|
|
59
|
+
const schema = S.TaggedUnion(
|
|
60
|
+
S.TaggedStruct("X", { x: S.String }),
|
|
61
|
+
S.TaggedStruct("Y", { y: S.Number })
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
expect(schema.tags.literals).toEqual(["X", "Y"])
|
|
65
|
+
expectTypeOf(schema.tags.literals).toMatchTypeOf<readonly ["X", "Y"]>()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("TaggedUnion tags.pick returns a subset of the tag literals", () => {
|
|
69
|
+
const schema = S.TaggedUnion(
|
|
70
|
+
S.TaggedStruct("A", { a: S.String }),
|
|
71
|
+
S.TaggedStruct("B", { b: S.Number }),
|
|
72
|
+
S.TaggedStruct("C", { c: S.Boolean })
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const subset = schema.tags.pick(["A", "C"])
|
|
76
|
+
expect(subset.literals).toEqual(["A", "C"])
|
|
77
|
+
expect(S.decodeSync(subset)("A")).toBe("A")
|
|
78
|
+
expect(S.decodeSync(subset)("C")).toBe("C")
|
|
79
|
+
expect(() => S.decodeUnknownSync(subset)("B")).toThrow()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test("tags standalone function extracts tags from member schemas", () => {
|
|
83
|
+
const members = [
|
|
84
|
+
S.TaggedStruct("Foo", { foo: S.String }),
|
|
85
|
+
S.TaggedStruct("Bar", { bar: S.Number })
|
|
86
|
+
] as const
|
|
87
|
+
|
|
88
|
+
const tagSchema = S.tags(members)
|
|
89
|
+
expect(tagSchema.literals).toEqual(["Foo", "Bar"])
|
|
90
|
+
expect(S.decodeSync(tagSchema)("Foo")).toBe("Foo")
|
|
91
|
+
expect(S.decodeSync(tagSchema)("Bar")).toBe("Bar")
|
|
92
|
+
expect(() => S.decodeUnknownSync(tagSchema)("Baz")).toThrow()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test("ExtendTaggedUnion adds tags to an existing Union", () => {
|
|
96
|
+
const union = S.Union([
|
|
97
|
+
S.TaggedStruct("P", { p: S.String }),
|
|
98
|
+
S.TaggedStruct("Q", { q: S.Number })
|
|
99
|
+
])
|
|
100
|
+
const extended = S.ExtendTaggedUnion(union)
|
|
101
|
+
|
|
102
|
+
expect(extended.tags.literals).toEqual(["P", "Q"])
|
|
103
|
+
expect(S.decodeSync(extended.tags)("P")).toBe("P")
|
|
104
|
+
expect(S.decodeSync(extended.tags)("Q")).toBe("Q")
|
|
105
|
+
expect(() => S.decodeUnknownSync(extended.tags)("R")).toThrow()
|
|
106
|
+
expect(extended.cases["P"].fields._tag.ast.literal).toBe("P")
|
|
107
|
+
expect(extended.guards.P({ _tag: "P", p: "ok" })).toBe(true)
|
|
108
|
+
expect(extended.guards.P({ _tag: "Q", q: 1 })).toBe(false)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test("TaggedUnion match dispatches on _tag", () => {
|
|
112
|
+
const schema = S.TaggedUnion(
|
|
113
|
+
S.TaggedStruct("A", { a: S.String }),
|
|
114
|
+
S.TaggedStruct("B", { b: S.Number })
|
|
115
|
+
)
|
|
116
|
+
type T = S.Schema.Type<typeof schema>
|
|
117
|
+
|
|
118
|
+
const matcher = schema.match({
|
|
119
|
+
A: (v) => `got A: ${v.a}`,
|
|
120
|
+
B: (v) => `got B: ${v.b}`
|
|
121
|
+
})
|
|
122
|
+
expect(matcher({ _tag: "A", a: "hello" } as T)).toBe("got A: hello")
|
|
123
|
+
expect(matcher({ _tag: "B", b: 42 } as T)).toBe("got B: 42")
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test("TaggedUnion with single member", () => {
|
|
127
|
+
const schema = S.TaggedUnion(
|
|
128
|
+
S.TaggedStruct("Only", { val: S.String })
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
expect(schema.tags.literals).toEqual(["Only"])
|
|
132
|
+
expect(S.decodeSync(schema.tags)("Only")).toBe("Only")
|
|
133
|
+
expect(() => S.decodeUnknownSync(schema.tags)("Other")).toThrow()
|
|
134
|
+
expect(schema.guards.Only({ _tag: "Only", val: "x" })).toBe(true)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test("TaggedUnion tags type is narrowed to the exact tag literals", () => {
|
|
138
|
+
const schema = S.TaggedUnion(
|
|
139
|
+
S.TaggedStruct("Alpha", { a: S.String }),
|
|
140
|
+
S.TaggedStruct("Beta", { b: S.Number }),
|
|
141
|
+
S.TaggedStruct("Gamma", { c: S.Boolean })
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
type Tags = S.Schema.Type<typeof schema.tags>
|
|
145
|
+
expectTypeOf<Tags>().toEqualTypeOf<"Alpha" | "Beta" | "Gamma">()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test("TaggedUnion with encodeKeys renaming a non-tag key", () => {
|
|
149
|
+
const MemberA = S.TaggedStruct("A", { firstName: S.String }).pipe(
|
|
150
|
+
S.encodeKeys({ firstName: "first_name" })
|
|
151
|
+
)
|
|
152
|
+
const MemberB = S.TaggedStruct("B", { lastName: S.Number }).pipe(
|
|
153
|
+
S.encodeKeys({ lastName: "last_name" })
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
const schema = S.TaggedUnion(MemberA, MemberB)
|
|
157
|
+
|
|
158
|
+
expect(schema.tags.literals).toEqual(["A", "B"])
|
|
159
|
+
expect(S.decodeSync(schema.tags)("A")).toBe("A")
|
|
160
|
+
expect(S.decodeSync(schema.tags)("B")).toBe("B")
|
|
161
|
+
|
|
162
|
+
// decode from encoded (snake_case) to decoded (camelCase)
|
|
163
|
+
const decoded = S.decodeUnknownSync(schema)({ _tag: "A", first_name: "Alice" })
|
|
164
|
+
expect(decoded).toEqual({ _tag: "A", firstName: "Alice" })
|
|
165
|
+
|
|
166
|
+
const decoded2 = S.decodeUnknownSync(schema)({ _tag: "B", last_name: 42 })
|
|
167
|
+
expect(decoded2).toEqual({ _tag: "B", lastName: 42 })
|
|
168
|
+
|
|
169
|
+
// encode back to snake_case
|
|
170
|
+
type T = S.Schema.Type<typeof schema>
|
|
171
|
+
const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" } as T)
|
|
172
|
+
expect(encoded).toEqual({ _tag: "A", first_name: "Alice" })
|
|
173
|
+
|
|
174
|
+
// guards work on decoded values
|
|
175
|
+
expect(schema.guards.A({ _tag: "A", firstName: "Alice" })).toBe(true)
|
|
176
|
+
expect(schema.guards.A({ _tag: "B", lastName: 42 })).toBe(false)
|
|
177
|
+
expect(schema.guards.B({ _tag: "B", lastName: 42 })).toBe(true)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test("TaggedUnion with TaggedClass members", () => {
|
|
181
|
+
class Foo extends S.TaggedClass<Foo>()("Foo", { name: S.String }) {}
|
|
182
|
+
class Bar extends S.TaggedClass<Bar>()("Bar", { count: S.Number }) {}
|
|
183
|
+
|
|
184
|
+
const schema = S.TaggedUnion(Foo, Bar)
|
|
185
|
+
|
|
186
|
+
expect(schema.tags.literals).toEqual(["Foo", "Bar"])
|
|
187
|
+
expect(S.decodeSync(schema.tags)("Foo")).toBe("Foo")
|
|
188
|
+
expect(S.decodeSync(schema.tags)("Bar")).toBe("Bar")
|
|
189
|
+
expect(() => S.decodeUnknownSync(schema.tags)("Baz")).toThrow()
|
|
190
|
+
|
|
191
|
+
const decoded = S.decodeUnknownSync(schema)({ _tag: "Foo", name: "Alice" })
|
|
192
|
+
expect(decoded).toBeInstanceOf(Foo)
|
|
193
|
+
expect(decoded).toEqual(new Foo({ name: "Alice" }))
|
|
194
|
+
|
|
195
|
+
const decoded2 = S.decodeUnknownSync(schema)({ _tag: "Bar", count: 3 })
|
|
196
|
+
expect(decoded2).toBeInstanceOf(Bar)
|
|
197
|
+
expect(decoded2).toEqual(new Bar({ count: 3 }))
|
|
198
|
+
|
|
199
|
+
expect(schema.guards.Foo(new Foo({ name: "Alice" }))).toBe(true)
|
|
200
|
+
expect(schema.guards.Foo(new Bar({ count: 3 }))).toBe(false)
|
|
201
|
+
expect(schema.guards.Bar(new Bar({ count: 3 }))).toBe(true)
|
|
202
|
+
})
|