effect-app 4.0.0-beta.6 → 4.0.0-beta.60
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 +265 -0
- package/dist/Config.d.ts +7 -0
- package/dist/Config.d.ts.map +1 -0
- package/dist/Config.js +6 -0
- package/dist/ConfigProvider.d.ts +39 -0
- package/dist/ConfigProvider.d.ts.map +1 -0
- package/dist/ConfigProvider.js +42 -0
- package/dist/{ServiceMap.d.ts → Context.d.ts} +9 -12
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +87 -0
- package/dist/Effect.d.ts +8 -7
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +3 -2
- package/dist/Layer.d.ts +5 -4
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +1 -1
- package/dist/Operations.d.ts +51 -15
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Pure.d.ts +2 -2
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +13 -13
- package/dist/Schema/Class.d.ts +39 -1
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +89 -12
- package/dist/Schema/SpecialJsonSchema.d.ts +40 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +199 -0
- package/dist/Schema/SpecialOpenApi.d.ts +30 -0
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
- package/dist/Schema/SpecialOpenApi.js +120 -0
- package/dist/Schema/brand.d.ts +8 -5
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +1 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +4 -3
- package/dist/Schema/ext.d.ts +142 -44
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +145 -35
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +6 -4
- package/dist/Schema/numbers.d.ts +8 -8
- package/dist/Schema/numbers.js +2 -2
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +3 -2
- package/dist/Schema.d.ts +21 -54
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +43 -64
- package/dist/client/apiClientFactory.d.ts +3 -3
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +12 -13
- package/dist/client/errors.d.ts +8 -0
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +35 -10
- package/dist/client/makeClient.d.ts +13 -12
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +5 -2
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +5 -5
- package/dist/ids.d.ts +1 -1
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -9
- package/dist/middleware.d.ts +2 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +3 -3
- package/dist/rpc/MiddlewareMaker.d.ts +4 -3
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +6 -5
- package/dist/rpc/RpcContextMap.d.ts +2 -2
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +4 -3
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/utils/gen.d.ts +1 -1
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +2 -2
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +3 -3
- package/dist/utils.d.ts +18 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -5
- package/package.json +29 -17
- package/src/Config.ts +14 -0
- package/src/ConfigProvider.ts +48 -0
- package/src/{ServiceMap.ts → Context.ts} +16 -17
- package/src/Effect.ts +11 -9
- package/src/Layer.ts +5 -4
- package/src/Pure.ts +17 -18
- package/src/Schema/Class.ts +114 -16
- package/src/Schema/SpecialJsonSchema.ts +216 -0
- package/src/Schema/SpecialOpenApi.ts +126 -0
- package/src/Schema/brand.ts +13 -7
- package/src/Schema/email.ts +4 -2
- package/src/Schema/ext.ts +222 -57
- package/src/Schema/moreStrings.ts +10 -6
- package/src/Schema/numbers.ts +2 -2
- package/src/Schema/phoneNumber.ts +3 -1
- package/src/Schema.ts +79 -103
- package/src/client/apiClientFactory.ts +16 -19
- package/src/client/errors.ts +46 -12
- package/src/client/makeClient.ts +32 -12
- package/src/http/Request.ts +7 -4
- package/src/ids.ts +1 -1
- package/src/index.ts +6 -9
- package/src/middleware.ts +2 -2
- package/src/rpc/MiddlewareMaker.ts +7 -6
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +5 -4
- package/src/utils/gen.ts +1 -1
- package/src/utils/logger.ts +2 -2
- package/src/utils.ts +26 -4
- package/test/dist/moreStrings.test.d.ts.map +1 -0
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/special.test.d.ts.map +1 -0
- package/test/moreStrings.test.ts +17 -0
- package/test/rpc.test.ts +26 -5
- package/test/schema.test.ts +292 -1
- package/test/special.test.ts +525 -0
- package/test/utils.test.ts +1 -1
- package/tsconfig.base.json +0 -1
- package/tsconfig.json +0 -1
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/dist/Struct.d.ts +0 -44
- package/dist/Struct.d.ts.map +0 -1
- package/dist/Struct.js +0 -29
- package/src/Struct.ts +0 -54
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { Option, Predicate, Schema, SchemaGetter } from "effect"
|
|
2
|
+
import { InvalidStateError, LoginError, NotFoundError, NotLoggedInError, OptimisticConcurrencyException, ServiceUnavailableError, UnauthorizedError, ValidationError } from "effect-app/client/errors"
|
|
3
|
+
import { Class, TaggedClass } from "effect-app/Schema/Class"
|
|
4
|
+
import { specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema"
|
|
5
|
+
import { deduplicateOpenApiSchemas } from "effect-app/Schema/SpecialOpenApi"
|
|
6
|
+
import * as S from "effect/Schema"
|
|
7
|
+
import { describe, expect, it } from "vitest"
|
|
8
|
+
|
|
9
|
+
describe("Class", () => {
|
|
10
|
+
it("encoding accepts plain objects matching the struct (Fields argument)", () => {
|
|
11
|
+
class A extends Class<A>("A")({ a: S.String }) {}
|
|
12
|
+
|
|
13
|
+
// Encoding a class instance still works
|
|
14
|
+
expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" })
|
|
15
|
+
|
|
16
|
+
// Encoding a plain object matching the struct now succeeds
|
|
17
|
+
expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" })
|
|
18
|
+
|
|
19
|
+
// Encoding null still fails
|
|
20
|
+
expect(() => S.encodeUnknownSync(A)(null)).toThrow()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it("encoding accepts plain objects matching the struct (Struct argument)", () => {
|
|
24
|
+
class A extends Class<A>("A")(S.Struct({ a: S.String })) {}
|
|
25
|
+
|
|
26
|
+
expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" })
|
|
27
|
+
expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" })
|
|
28
|
+
expect(() => S.encodeUnknownSync(A)(null)).toThrow()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it("decoding still works normally", () => {
|
|
32
|
+
class A extends Class<A>("A")({ a: S.String }) {}
|
|
33
|
+
|
|
34
|
+
const decoded = S.decodeUnknownSync(A)({ a: "hello" })
|
|
35
|
+
expect(decoded).toBeInstanceOf(A)
|
|
36
|
+
expect((decoded as A).a).toBe("hello")
|
|
37
|
+
|
|
38
|
+
expect(() => S.decodeUnknownSync(A)(null)).toThrow()
|
|
39
|
+
expect(() => S.decodeUnknownSync(A)({ a: 1 })).toThrow()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it("rejects values that don't match the struct", () => {
|
|
43
|
+
class A extends Class<A>("A")({ a: S.String }) {}
|
|
44
|
+
|
|
45
|
+
expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow()
|
|
46
|
+
expect(() => S.encodeUnknownSync(A)("not an object")).toThrow()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("returns a class constructor — new and instanceof work", () => {
|
|
50
|
+
class A extends Class<A>("A")({ a: S.String }) {}
|
|
51
|
+
|
|
52
|
+
const instance = new A({ a: "hello" })
|
|
53
|
+
expect(instance).toBeInstanceOf(A)
|
|
54
|
+
expect(instance.a).toBe("hello")
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it("preserves fields and identifier", () => {
|
|
58
|
+
class A extends Class<A>("A")({ a: S.String, b: S.Number }) {}
|
|
59
|
+
|
|
60
|
+
expect(A.identifier).toBe("A")
|
|
61
|
+
expect(Object.keys(A.fields)).toStrictEqual(["a", "b"])
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe("Class constructor", () => {
|
|
66
|
+
it("works as a base class — new, instanceof, encoding plain objects", () => {
|
|
67
|
+
class A extends Class<A>("A")({ a: S.String }) {}
|
|
68
|
+
|
|
69
|
+
// Construction
|
|
70
|
+
const instance = new A({ a: "hello" })
|
|
71
|
+
expect(instance).toBeInstanceOf(A)
|
|
72
|
+
expect(instance.a).toBe("hello")
|
|
73
|
+
|
|
74
|
+
// Encoding a class instance
|
|
75
|
+
expect(S.encodeUnknownSync(A)(instance)).toStrictEqual({ a: "hello" })
|
|
76
|
+
|
|
77
|
+
// Encoding a plain object
|
|
78
|
+
expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" })
|
|
79
|
+
|
|
80
|
+
// Encoding invalid input fails
|
|
81
|
+
expect(() => S.encodeUnknownSync(A)(null)).toThrow()
|
|
82
|
+
expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it("decoding works normally", () => {
|
|
86
|
+
class A extends Class<A>("A")({ a: S.String }) {}
|
|
87
|
+
|
|
88
|
+
const decoded = S.decodeUnknownSync(A)({ a: "hello" })
|
|
89
|
+
expect(decoded).toBeInstanceOf(A)
|
|
90
|
+
expect((decoded as A).a).toBe("hello")
|
|
91
|
+
|
|
92
|
+
expect(() => S.decodeUnknownSync(A)({ a: 1 })).toThrow()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it("exposes fields, identifier, pick, omit", () => {
|
|
96
|
+
class A extends Class<A>("A")({ a: S.String, b: S.Number }) {}
|
|
97
|
+
|
|
98
|
+
expect(A.identifier).toBe("A")
|
|
99
|
+
expect(Object.keys(A.fields)).toStrictEqual(["a", "b"])
|
|
100
|
+
expect(A.pick("a")).toStrictEqual({ a: A.fields.a })
|
|
101
|
+
expect(A.omit("b")).toStrictEqual({ a: A.fields.a })
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
describe("TaggedClass constructor", () => {
|
|
106
|
+
it("works as a base class with _tag — new, instanceof, encoding plain objects", () => {
|
|
107
|
+
class Circle extends TaggedClass<Circle>()("Circle", { radius: S.Number }) {}
|
|
108
|
+
|
|
109
|
+
// Construction
|
|
110
|
+
const instance = new Circle({ radius: 5 })
|
|
111
|
+
expect(instance).toBeInstanceOf(Circle)
|
|
112
|
+
expect(instance._tag).toBe("Circle")
|
|
113
|
+
expect(instance.radius).toBe(5)
|
|
114
|
+
|
|
115
|
+
// Encoding a class instance
|
|
116
|
+
expect(S.encodeUnknownSync(Circle)(instance)).toStrictEqual({ _tag: "Circle", radius: 5 })
|
|
117
|
+
|
|
118
|
+
// Encoding a plain object
|
|
119
|
+
expect(S.encodeUnknownSync(Circle)({ _tag: "Circle", radius: 10 })).toStrictEqual({ _tag: "Circle", radius: 10 })
|
|
120
|
+
|
|
121
|
+
// Encoding invalid input fails
|
|
122
|
+
expect(() => S.encodeUnknownSync(Circle)(null)).toThrow()
|
|
123
|
+
expect(() => S.encodeUnknownSync(Circle)({ _tag: "Circle", radius: "nope" })).toThrow()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it("decoding works normally", () => {
|
|
127
|
+
class Circle extends TaggedClass<Circle>()("Circle", { radius: S.Number }) {}
|
|
128
|
+
|
|
129
|
+
const decoded = S.decodeUnknownSync(Circle)({ _tag: "Circle", radius: 5 })
|
|
130
|
+
expect(decoded).toBeInstanceOf(Circle)
|
|
131
|
+
expect((decoded as Circle).radius).toBe(5)
|
|
132
|
+
expect((decoded as Circle)._tag).toBe("Circle")
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it("exposes fields, identifier, pick, omit", () => {
|
|
136
|
+
class Circle extends TaggedClass<Circle>()("Circle", { radius: S.Number }) {}
|
|
137
|
+
|
|
138
|
+
expect(Circle.identifier).toBe("Circle")
|
|
139
|
+
expect(Object.keys(Circle.fields)).toContain("_tag")
|
|
140
|
+
expect(Object.keys(Circle.fields)).toContain("radius")
|
|
141
|
+
expect(Circle.pick("radius")).toStrictEqual({ radius: Circle.fields.radius })
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
describe("TaggedError", () => {
|
|
146
|
+
it("InvalidStateError toString includes the message", () => {
|
|
147
|
+
const error = new InvalidStateError("something went wrong")
|
|
148
|
+
expect(error.toString()).toContain("something went wrong")
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it("NotFoundError toString includes the message", () => {
|
|
152
|
+
const error = new NotFoundError({ type: "User", id: "123" })
|
|
153
|
+
expect(error.toString()).toContain("Didn't find User")
|
|
154
|
+
expect(error.toString()).toContain("123")
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it("ServiceUnavailableError toString includes the message", () => {
|
|
158
|
+
const error = new ServiceUnavailableError("service down")
|
|
159
|
+
expect(error.toString()).toContain("service down")
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("ValidationError toString includes the message", () => {
|
|
163
|
+
const error = new ValidationError({ errors: ["field required"] })
|
|
164
|
+
expect(error.toString()).toContain("Validation failed")
|
|
165
|
+
expect(error.toString()).toContain("field required")
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it("NotLoggedInError toString includes the message", () => {
|
|
169
|
+
const error = new NotLoggedInError("not logged in")
|
|
170
|
+
expect(error.toString()).toContain("not logged in")
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it("LoginError toString includes the message", () => {
|
|
174
|
+
const error = new LoginError("login failed")
|
|
175
|
+
expect(error.toString()).toContain("login failed")
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it("UnauthorizedError toString includes the message", () => {
|
|
179
|
+
const error = new UnauthorizedError("forbidden")
|
|
180
|
+
expect(error.toString()).toContain("forbidden")
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it("OptimisticConcurrencyException toString includes the message", () => {
|
|
184
|
+
const error = new OptimisticConcurrencyException({ message: "conflict" })
|
|
185
|
+
expect(error.toString()).toContain("conflict")
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it("OptimisticConcurrencyException from details toString includes the message", () => {
|
|
189
|
+
const error = new OptimisticConcurrencyException({ type: "User", id: "123", code: 409 })
|
|
190
|
+
expect(error.toString()).toContain("Existing User 123 record changed")
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe("SpecialJsonSchema", () => {
|
|
195
|
+
it("nullable to optional — from NullOr", () => {
|
|
196
|
+
const nullableDecodedUndefinedEncoded = (schema: Schema.Top) => {
|
|
197
|
+
const isNullableSchema = "members" in schema
|
|
198
|
+
&& globalThis.Array.isArray((schema as any).members)
|
|
199
|
+
&& (schema as any).members.length === 2
|
|
200
|
+
&& (schema as any).members.some((member: any) => member.ast._tag === "Null")
|
|
201
|
+
|
|
202
|
+
const nullableMembers = isNullableSchema ? (schema as any).members as ReadonlyArray<Schema.Top> : undefined
|
|
203
|
+
const innerSchema = nullableMembers
|
|
204
|
+
? nullableMembers.find((member: any) => member.ast._tag !== "Null")!
|
|
205
|
+
: schema
|
|
206
|
+
|
|
207
|
+
const nullableSchema = isNullableSchema ? schema : Schema.NullOr(schema)
|
|
208
|
+
|
|
209
|
+
return nullableSchema.pipe(
|
|
210
|
+
Schema.encodeTo(Schema.optionalKey(innerSchema), {
|
|
211
|
+
decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)),
|
|
212
|
+
encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull))
|
|
213
|
+
})
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const fromNullOr = nullableDecodedUndefinedEncoded(Schema.NullOr(Schema.String))
|
|
218
|
+
const structFromNullOr = Schema.Struct({ status: fromNullOr })
|
|
219
|
+
|
|
220
|
+
const encode = Schema.encodeUnknownSync(structFromNullOr as any)
|
|
221
|
+
const encodedNull = encode({ status: null }) as any
|
|
222
|
+
expect("status" in encodedNull).toBe(false)
|
|
223
|
+
expect(encode({ status: "test" })).toStrictEqual({ status: "test" })
|
|
224
|
+
|
|
225
|
+
const decode = Schema.decodeUnknownSync(structFromNullOr as any)
|
|
226
|
+
expect(decode({})).toStrictEqual({ status: null })
|
|
227
|
+
expect(decode({ status: "test" })).toStrictEqual({ status: "test" })
|
|
228
|
+
|
|
229
|
+
const doc = specialJsonSchemaDocument(structFromNullOr)
|
|
230
|
+
expect(doc).toStrictEqual({
|
|
231
|
+
dialect: "draft-2020-12",
|
|
232
|
+
schema: {
|
|
233
|
+
"type": "object",
|
|
234
|
+
"properties": {
|
|
235
|
+
"status": { "type": "string" }
|
|
236
|
+
},
|
|
237
|
+
"additionalProperties": false
|
|
238
|
+
},
|
|
239
|
+
definitions: {}
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it("identifies X universally — deduplicates same-fingerprint references", () => {
|
|
244
|
+
const X = Schema.String.annotate({ title: "X", identifier: "X" })
|
|
245
|
+
|
|
246
|
+
const s = Schema.Struct({
|
|
247
|
+
a: Schema.NullOr(X).pipe(
|
|
248
|
+
Schema.encodeTo(Schema.optionalKey(X), {
|
|
249
|
+
decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)),
|
|
250
|
+
encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull))
|
|
251
|
+
})
|
|
252
|
+
),
|
|
253
|
+
b: Schema.NullOr(X).pipe(
|
|
254
|
+
Schema.encodeTo(Schema.optionalKey(X), {
|
|
255
|
+
decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)),
|
|
256
|
+
encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull))
|
|
257
|
+
})
|
|
258
|
+
),
|
|
259
|
+
c: Schema.NullOr(X),
|
|
260
|
+
d: X,
|
|
261
|
+
e: X.pipe(Schema.optionalKey)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
const doc = specialJsonSchemaDocument(s)
|
|
265
|
+
expect(doc).toStrictEqual({
|
|
266
|
+
dialect: "draft-2020-12",
|
|
267
|
+
schema: {
|
|
268
|
+
"type": "object",
|
|
269
|
+
"properties": {
|
|
270
|
+
"a": { "$ref": "#/$defs/X" },
|
|
271
|
+
"b": { "$ref": "#/$defs/X" },
|
|
272
|
+
"c": {
|
|
273
|
+
"anyOf": [
|
|
274
|
+
{ "$ref": "#/$defs/X" },
|
|
275
|
+
{ "type": "null" }
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
"d": { "$ref": "#/$defs/X" },
|
|
279
|
+
"e": { "$ref": "#/$defs/X" }
|
|
280
|
+
},
|
|
281
|
+
"required": ["c", "d"],
|
|
282
|
+
"additionalProperties": false
|
|
283
|
+
},
|
|
284
|
+
definitions: {
|
|
285
|
+
X: {
|
|
286
|
+
"type": "string",
|
|
287
|
+
"title": "X"
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it("shared annotated schema via helper — deduplicates", () => {
|
|
294
|
+
const X = Schema.String.annotate({ title: "X", identifier: "X" })
|
|
295
|
+
|
|
296
|
+
const cache = new WeakMap()
|
|
297
|
+
const nullableDecodedUndefinedEncoded = (schema: Schema.Top) => {
|
|
298
|
+
const isNullableSchema = "members" in schema
|
|
299
|
+
&& globalThis.Array.isArray((schema as any).members)
|
|
300
|
+
&& (schema as any).members.length === 2
|
|
301
|
+
&& (schema as any).members.some((member: any) => member.ast._tag === "Null")
|
|
302
|
+
|
|
303
|
+
const nullableMembers = isNullableSchema ? (schema as any).members as ReadonlyArray<Schema.Top> : undefined
|
|
304
|
+
const innerSchema = nullableMembers
|
|
305
|
+
? nullableMembers.find((member: any) => member.ast._tag !== "Null")!
|
|
306
|
+
: schema
|
|
307
|
+
|
|
308
|
+
const cached = cache.get(innerSchema.ast)
|
|
309
|
+
if (cached !== undefined) return cached
|
|
310
|
+
|
|
311
|
+
const nullableSchema = isNullableSchema ? schema : Schema.NullOr(schema)
|
|
312
|
+
const out = nullableSchema.pipe(
|
|
313
|
+
Schema.encodeTo(Schema.optionalKey(innerSchema), {
|
|
314
|
+
decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)),
|
|
315
|
+
encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull))
|
|
316
|
+
})
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
cache.set(innerSchema.ast, out)
|
|
320
|
+
return out
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const structWithShared = Schema.Struct({
|
|
324
|
+
a: nullableDecodedUndefinedEncoded(X),
|
|
325
|
+
b: nullableDecodedUndefinedEncoded(Schema.NullOr(X)),
|
|
326
|
+
c: Schema.NullOr(X),
|
|
327
|
+
d: X,
|
|
328
|
+
e: X.pipe(Schema.optionalKey)
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
const doc = specialJsonSchemaDocument(structWithShared)
|
|
332
|
+
expect(doc).toStrictEqual({
|
|
333
|
+
dialect: "draft-2020-12",
|
|
334
|
+
schema: {
|
|
335
|
+
"type": "object",
|
|
336
|
+
"properties": {
|
|
337
|
+
"a": { "$ref": "#/$defs/X" },
|
|
338
|
+
"b": { "$ref": "#/$defs/X" },
|
|
339
|
+
"c": {
|
|
340
|
+
"anyOf": [
|
|
341
|
+
{ "$ref": "#/$defs/X" },
|
|
342
|
+
{ "type": "null" }
|
|
343
|
+
]
|
|
344
|
+
},
|
|
345
|
+
"d": { "$ref": "#/$defs/X" },
|
|
346
|
+
"e": { "$ref": "#/$defs/X" }
|
|
347
|
+
},
|
|
348
|
+
"required": ["c", "d"],
|
|
349
|
+
"additionalProperties": false
|
|
350
|
+
},
|
|
351
|
+
definitions: {
|
|
352
|
+
X: {
|
|
353
|
+
"type": "string",
|
|
354
|
+
"title": "X"
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
describe("SpecialOpenApi", () => {
|
|
362
|
+
it("deduplicates identical components.schemas entries with same base identifier", () => {
|
|
363
|
+
const spec = {
|
|
364
|
+
openapi: "3.1.0",
|
|
365
|
+
info: { title: "Test", version: "1.0" },
|
|
366
|
+
paths: {
|
|
367
|
+
"/foo": {
|
|
368
|
+
get: {
|
|
369
|
+
responses: {
|
|
370
|
+
200: {
|
|
371
|
+
content: {
|
|
372
|
+
"application/json": {
|
|
373
|
+
schema: { $ref: "#/components/schemas/X" }
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"/bar": {
|
|
381
|
+
get: {
|
|
382
|
+
responses: {
|
|
383
|
+
200: {
|
|
384
|
+
content: {
|
|
385
|
+
"application/json": {
|
|
386
|
+
schema: { $ref: "#/components/schemas/X1" }
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
components: {
|
|
395
|
+
schemas: {
|
|
396
|
+
X: { type: "string", title: "X" },
|
|
397
|
+
X1: { type: "string", title: "X" }
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const result = deduplicateOpenApiSchemas(spec) as any
|
|
403
|
+
|
|
404
|
+
// X1 should be removed, and $ref to X1 rewritten to X
|
|
405
|
+
expect(result.components.schemas).toStrictEqual({
|
|
406
|
+
X: { type: "string", title: "X" }
|
|
407
|
+
})
|
|
408
|
+
expect(
|
|
409
|
+
result.paths["/bar"].get.responses[200].content["application/json"].schema
|
|
410
|
+
)
|
|
411
|
+
.toStrictEqual({ $ref: "#/components/schemas/X" })
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
it("does not deduplicate entries with different representations", () => {
|
|
415
|
+
const spec = {
|
|
416
|
+
openapi: "3.1.0",
|
|
417
|
+
info: { title: "Test", version: "1.0" },
|
|
418
|
+
paths: {},
|
|
419
|
+
components: {
|
|
420
|
+
schemas: {
|
|
421
|
+
X: { type: "string", title: "X" },
|
|
422
|
+
X1: { type: "number", title: "X" }
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const result = deduplicateOpenApiSchemas(spec) as any
|
|
428
|
+
|
|
429
|
+
// Both should remain since they have different representations
|
|
430
|
+
expect(result.components.schemas).toStrictEqual({
|
|
431
|
+
X: { type: "string", title: "X" },
|
|
432
|
+
X1: { type: "number", title: "X" }
|
|
433
|
+
})
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
it("returns spec unchanged when no duplicates exist", () => {
|
|
437
|
+
const spec = {
|
|
438
|
+
openapi: "3.1.0",
|
|
439
|
+
info: { title: "Test", version: "1.0" },
|
|
440
|
+
paths: {},
|
|
441
|
+
components: {
|
|
442
|
+
schemas: {
|
|
443
|
+
Foo: { type: "string" },
|
|
444
|
+
Bar: { type: "number" }
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const result = deduplicateOpenApiSchemas(spec)
|
|
450
|
+
expect(result).toBe(spec) // same reference, no cloning needed
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
it("rewrites nested $ref pointers in allOf/anyOf/oneOf", () => {
|
|
454
|
+
const spec = {
|
|
455
|
+
openapi: "3.1.0",
|
|
456
|
+
info: { title: "Test", version: "1.0" },
|
|
457
|
+
paths: {
|
|
458
|
+
"/baz": {
|
|
459
|
+
post: {
|
|
460
|
+
requestBody: {
|
|
461
|
+
content: {
|
|
462
|
+
"application/json": {
|
|
463
|
+
schema: {
|
|
464
|
+
anyOf: [
|
|
465
|
+
{ $ref: "#/components/schemas/Y1" },
|
|
466
|
+
{ type: "null" }
|
|
467
|
+
]
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
components: {
|
|
476
|
+
schemas: {
|
|
477
|
+
Y: { type: "object", properties: { name: { type: "string" } } },
|
|
478
|
+
Y1: { type: "object", properties: { name: { type: "string" } } }
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const result = deduplicateOpenApiSchemas(spec) as any
|
|
484
|
+
|
|
485
|
+
expect(result.components.schemas).toStrictEqual({
|
|
486
|
+
Y: { type: "object", properties: { name: { type: "string" } } }
|
|
487
|
+
})
|
|
488
|
+
expect(
|
|
489
|
+
result.paths["/baz"].post.requestBody.content["application/json"].schema.anyOf[0]
|
|
490
|
+
)
|
|
491
|
+
.toStrictEqual({ $ref: "#/components/schemas/Y" })
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
it("rewrites $ref pointers inside definitions themselves", () => {
|
|
495
|
+
const spec = {
|
|
496
|
+
openapi: "3.1.0",
|
|
497
|
+
info: { title: "Test", version: "1.0" },
|
|
498
|
+
paths: {},
|
|
499
|
+
components: {
|
|
500
|
+
schemas: {
|
|
501
|
+
Inner: { type: "string" },
|
|
502
|
+
Inner1: { type: "string" },
|
|
503
|
+
Outer: {
|
|
504
|
+
type: "object",
|
|
505
|
+
properties: {
|
|
506
|
+
field: { $ref: "#/components/schemas/Inner1" }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const result = deduplicateOpenApiSchemas(spec) as any
|
|
514
|
+
|
|
515
|
+
expect(Object.keys(result.components.schemas)).toStrictEqual(["Inner", "Outer"])
|
|
516
|
+
expect(result.components.schemas.Outer.properties.field).toStrictEqual({
|
|
517
|
+
$ref: "#/components/schemas/Inner"
|
|
518
|
+
})
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
it("handles spec without components gracefully", () => {
|
|
522
|
+
const spec = { openapi: "3.1.0", info: { title: "Test", version: "1.0" }, paths: {} }
|
|
523
|
+
expect(deduplicateOpenApiSchemas(spec)).toBe(spec)
|
|
524
|
+
})
|
|
525
|
+
})
|
package/test/utils.test.ts
CHANGED
|
@@ -111,7 +111,7 @@ test("works with schema class", () => {
|
|
|
111
111
|
name: S.String,
|
|
112
112
|
state: S.Union([
|
|
113
113
|
S.Struct({ a: S.String, _tag: S.Literal("a") }),
|
|
114
|
-
S.Struct({ b: S.
|
|
114
|
+
S.Struct({ b: S.Finite, _tag: S.Literal("b") })
|
|
115
115
|
])
|
|
116
116
|
}) {}
|
|
117
117
|
|
package/tsconfig.base.json
CHANGED
package/tsconfig.json
CHANGED
package/dist/ServiceMap.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceMap.d.ts","sourceRoot":"","sources":["../src/ServiceMap.ts"],"names":[],"mappings":"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"}
|
package/dist/ServiceMap.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
/**
|
|
3
|
-
* We're doing the long way around here with assignTag, TagBase & TagBaseTagged,
|
|
4
|
-
* because there's a typescript compiler issue where it will complain about Equal.symbol, and Hash.symbol not being accessible.
|
|
5
|
-
* https://github.com/microsoft/TypeScript/issues/52644
|
|
6
|
-
*/
|
|
7
|
-
import { Layer } from "effect";
|
|
8
|
-
import * as ServiceMap from "effect/ServiceMap";
|
|
9
|
-
export * from "effect/ServiceMap";
|
|
10
|
-
// export interface OpaqueMake<Self extends object, in out Shape extends object, E, R>
|
|
11
|
-
// extends ServiceMap.Service<Self, Self>
|
|
12
|
-
// {
|
|
13
|
-
// // temp while sorting out https://github.com/Effect-TS/effect-smol/pull/1534
|
|
14
|
-
// of(self: Shape): Self
|
|
15
|
-
// serviceMap2(self: Shape): ServiceMap.ServiceMap<Self>
|
|
16
|
-
// // a version that leverages the Shape -> Self conversion
|
|
17
|
-
// toLayer: {
|
|
18
|
-
// <E, R>(
|
|
19
|
-
// eff: Effect.Effect<Shape, E, R>
|
|
20
|
-
// ): Layer.Layer<Self, E, Exclude<R, Scope.Scope>>
|
|
21
|
-
// (): Layer.Layer<Self, E, Exclude<R, Scope.Scope>>
|
|
22
|
-
// }
|
|
23
|
-
// }
|
|
24
|
-
export function assignTag(key, creationError) {
|
|
25
|
-
return (cls) => {
|
|
26
|
-
const tag = ServiceMap.Service(key);
|
|
27
|
-
let fields = tag;
|
|
28
|
-
if (Reflect.ownKeys(cls).includes("key")) {
|
|
29
|
-
const { key, ...rest } = tag;
|
|
30
|
-
fields = rest;
|
|
31
|
-
}
|
|
32
|
-
const t = Object.assign(cls, Object.getPrototypeOf(tag), fields);
|
|
33
|
-
if (!creationError) {
|
|
34
|
-
const limit = Error.stackTraceLimit;
|
|
35
|
-
Error.stackTraceLimit = 2;
|
|
36
|
-
creationError = new Error();
|
|
37
|
-
Error.stackTraceLimit = limit;
|
|
38
|
-
}
|
|
39
|
-
// the stack is used to get the location of the tag definition, if a service is not found in the registry
|
|
40
|
-
Object.defineProperty(t, "stack", {
|
|
41
|
-
get() {
|
|
42
|
-
return creationError.stack;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
return t;
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Only use this in very specific cases where using dependencies directly is prefered, like inside command handlers.
|
|
50
|
-
*/
|
|
51
|
-
export const proxify = (Tag) => () => {
|
|
52
|
-
const cache = new Map();
|
|
53
|
-
const done = new Proxy(Tag, {
|
|
54
|
-
get(_target, prop, _receiver) {
|
|
55
|
-
if (prop === "use") {
|
|
56
|
-
// @ts-expect-error abc
|
|
57
|
-
return (body) => Tag.use(body);
|
|
58
|
-
}
|
|
59
|
-
if (prop in Tag) {
|
|
60
|
-
return Tag[prop];
|
|
61
|
-
}
|
|
62
|
-
if (cache.has(prop)) {
|
|
63
|
-
return cache.get(prop);
|
|
64
|
-
}
|
|
65
|
-
const fn = (...args) => Tag.use((s) => s[prop](...args));
|
|
66
|
-
const cn = Tag.use((s) => s[prop]);
|
|
67
|
-
// @effect-diagnostics effect/floatingEffect:off
|
|
68
|
-
Object.assign(fn, cn);
|
|
69
|
-
Object.setPrototypeOf(fn, Object.getPrototypeOf(cn));
|
|
70
|
-
cache.set(prop, fn);
|
|
71
|
-
return fn;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
return done;
|
|
75
|
-
};
|
|
76
|
-
export const TypeId = "~ServiceMap.Opaque";
|
|
77
|
-
// export interface OpaqueClassMake<Self extends object, in out Identifier extends string, Shape extends object, E, R>
|
|
78
|
-
// extends OpaqueMake<Self, Shape, E, R>
|
|
79
|
-
// {
|
|
80
|
-
// new(_: never): Shape & { readonly [TypeId]: Identifier }
|
|
81
|
-
// readonly key: Identifier
|
|
82
|
-
// }
|
|
83
|
-
export const Opaque = () => (id, options) => {
|
|
84
|
-
const svc = ServiceMap.Service()(id, options);
|
|
85
|
-
return Object.assign(svc, {
|
|
86
|
-
toLayer: (eff) => {
|
|
87
|
-
return Layer.effect(svc, eff);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmljZU1hcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXJ2aWNlTWFwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHVEQUF1RDtBQUN2RDs7OztHQUlHO0FBRUgsT0FBTyxFQUFlLEtBQUssRUFBMEIsTUFBTSxRQUFRLENBQUE7QUFDbkUsT0FBTyxLQUFLLFVBQVUsTUFBTSxtQkFBbUIsQ0FBQTtBQUcvQyxjQUFjLG1CQUFtQixDQUFBO0FBY2pDLHNGQUFzRjtBQUN0RiwyQ0FBMkM7QUFDM0MsSUFBSTtBQUNKLGlGQUFpRjtBQUNqRiwwQkFBMEI7QUFDMUIsMERBQTBEO0FBQzFELDZEQUE2RDtBQUM3RCxlQUFlO0FBQ2YsY0FBYztBQUNkLHdDQUF3QztBQUN4Qyx1REFBdUQ7QUFDdkQsd0RBQXdEO0FBQ3hELE1BQU07QUFDTixJQUFJO0FBRUosTUFBTSxVQUFVLFNBQVMsQ0FDdkIsR0FBVyxFQUNYLGFBQXFCO0lBRXJCLE9BQU8sQ0FBbUIsR0FBTSxFQUFpQyxFQUFFO1FBQ2pFLE1BQU0sR0FBRyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQW9CLEdBQUcsQ0FBQyxDQUFBO1FBQ3RELElBQUksTUFBTSxHQUFHLEdBQUcsQ0FBQTtRQUNoQixJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDekMsTUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxHQUFHLEdBQUcsQ0FBQTtZQUM1QixNQUFNLEdBQUcsSUFBVyxDQUFBO1FBQ3RCLENBQUM7UUFDRCxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFBO1FBQ2hFLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUNuQixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMs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
|