effect-app 4.0.0-beta.271 → 4.0.0-beta.273
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 +13 -0
- package/dist/RequestContext.d.ts +8 -62
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +9 -9
- package/dist/Schema/Class.d.ts +9 -6
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +57 -11
- package/dist/Schema.d.ts +1 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +2 -2
- package/dist/builtin.d.ts +2 -14
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +1 -1
- package/dist/client/clientFor.d.ts +1 -0
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/makeClient.d.ts +7 -6
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +1 -1
- package/package.json +1 -1
- package/src/RequestContext.ts +14 -14
- package/src/Schema/Class.ts +107 -31
- package/src/Schema.ts +1 -1
- package/src/builtin-json.d.ts +24 -0
- package/src/builtin.ts +16 -21
- package/src/client/clientFor.ts +1 -0
- package/src/client/makeClient.ts +5 -6
- package/test/special.test.ts +232 -0
package/test/special.test.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { InvalidStateError, LoginError, NotFoundError, NotLoggedInError, OptimisticConcurrencyException, ServiceUnavailableError, UnauthorizedError, ValidationError } from "effect-app/client/errors"
|
|
2
|
+
import { RequestContext } from "effect-app/RequestContext"
|
|
2
3
|
import * as AppSchema from "effect-app/Schema"
|
|
3
4
|
import { Class } from "effect-app/Schema/Class"
|
|
4
5
|
import { flattenNestedAnyOf, flattenSimpleAllOf, specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema"
|
|
5
6
|
import { deduplicateOpenApiSchemas } from "effect-app/Schema/SpecialOpenApi"
|
|
7
|
+
import * as Effect from "effect/Effect"
|
|
6
8
|
import * as Option from "effect/Option"
|
|
7
9
|
import * as Predicate from "effect/Predicate"
|
|
8
10
|
import * as Schema from "effect/Schema"
|
|
@@ -10,6 +12,133 @@ import * as S from "effect/Schema"
|
|
|
10
12
|
import * as SchemaGetter from "effect/SchemaGetter"
|
|
11
13
|
import { describe, expect, it } from "vitest"
|
|
12
14
|
|
|
15
|
+
class _FacadeModel extends AppSchema.Class<_FacadeModel>("FacadeModel")({ value: S.String }) {
|
|
16
|
+
get upperValue() {
|
|
17
|
+
return this.value.toUpperCase()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static describe(self: { readonly value: string }) {
|
|
21
|
+
return `value:${self.value}`
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface FacadeModelEncoded {
|
|
26
|
+
readonly value: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface FacadeModelMake {
|
|
30
|
+
readonly value: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class FacadeModel extends AppSchema.OpaqueFacade<
|
|
34
|
+
FacadeModel,
|
|
35
|
+
FacadeModelEncoded,
|
|
36
|
+
FacadeModelMake,
|
|
37
|
+
never,
|
|
38
|
+
never
|
|
39
|
+
>()(_FacadeModel) {
|
|
40
|
+
declare readonly value: string
|
|
41
|
+
declare readonly upperValue: string
|
|
42
|
+
|
|
43
|
+
get decoratedValue() {
|
|
44
|
+
return `facade:${this.value}`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
label() {
|
|
48
|
+
return this.decoratedValue
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class _RenamedFacadeModel extends AppSchema.Class<_RenamedFacadeModel>("RenamedFacadeModel")({
|
|
53
|
+
internalName: S.String
|
|
54
|
+
}) {
|
|
55
|
+
get upperName() {
|
|
56
|
+
return this.internalName.toUpperCase()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface RenamedFacadeModelEncoded {
|
|
61
|
+
readonly external_name: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface RenamedFacadeModelMake {
|
|
65
|
+
readonly internalName: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class RenamedFacadeModel extends AppSchema.OpaqueFacade<
|
|
69
|
+
RenamedFacadeModel,
|
|
70
|
+
RenamedFacadeModelEncoded,
|
|
71
|
+
RenamedFacadeModelMake,
|
|
72
|
+
never,
|
|
73
|
+
never
|
|
74
|
+
>()(_RenamedFacadeModel.pipe(S.encodeKeys({ internalName: "external_name" }))) {
|
|
75
|
+
declare readonly internalName: string
|
|
76
|
+
declare readonly upperName: string
|
|
77
|
+
|
|
78
|
+
get decoratedName() {
|
|
79
|
+
return `facade:${this.internalName}`
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const _StructFacadeModel = AppSchema.Struct({ value: S.String })
|
|
84
|
+
|
|
85
|
+
interface StructFacadeModelEncoded {
|
|
86
|
+
readonly value: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface StructFacadeModelMake {
|
|
90
|
+
readonly value: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
class StructFacadeModel extends AppSchema.OpaqueFacade<
|
|
94
|
+
StructFacadeModel,
|
|
95
|
+
StructFacadeModelEncoded,
|
|
96
|
+
StructFacadeModelMake,
|
|
97
|
+
never,
|
|
98
|
+
never
|
|
99
|
+
>()(_StructFacadeModel) {
|
|
100
|
+
declare readonly value: string
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const _RenamedStructFacadeModel = AppSchema.Struct({ internalName: S.String }).pipe(
|
|
104
|
+
S.encodeKeys({ internalName: "external_name" })
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
interface RenamedStructFacadeModelEncoded {
|
|
108
|
+
readonly external_name: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface RenamedStructFacadeModelMake {
|
|
112
|
+
readonly internalName: string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
class RenamedStructFacadeModel extends AppSchema.OpaqueFacade<
|
|
116
|
+
RenamedStructFacadeModel,
|
|
117
|
+
RenamedStructFacadeModelEncoded,
|
|
118
|
+
RenamedStructFacadeModelMake,
|
|
119
|
+
never,
|
|
120
|
+
never
|
|
121
|
+
>()(_RenamedStructFacadeModel) {
|
|
122
|
+
declare readonly internalName: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const _RenamedOpaqueStructFacadeModel = AppSchema
|
|
126
|
+
.Opaque<
|
|
127
|
+
{ readonly internalName: string },
|
|
128
|
+
RenamedStructFacadeModelEncoded
|
|
129
|
+
>()(AppSchema.Struct({ internalName: S.String }))
|
|
130
|
+
.pipe(S.encodeKeys({ internalName: "external_name" }))
|
|
131
|
+
|
|
132
|
+
class RenamedOpaqueStructFacadeModel extends AppSchema.OpaqueFacade<
|
|
133
|
+
RenamedOpaqueStructFacadeModel,
|
|
134
|
+
RenamedStructFacadeModelEncoded,
|
|
135
|
+
RenamedStructFacadeModelMake,
|
|
136
|
+
never,
|
|
137
|
+
never
|
|
138
|
+
>()(_RenamedOpaqueStructFacadeModel) {
|
|
139
|
+
declare readonly internalName: string
|
|
140
|
+
}
|
|
141
|
+
|
|
13
142
|
describe("Class", () => {
|
|
14
143
|
describe("strict", () => {
|
|
15
144
|
it("encoding doesnt accepts plain objects matching the struct (Fields argument)", () => {
|
|
@@ -268,6 +397,109 @@ describe("TaggedStruct (opaque).copy", () => {
|
|
|
268
397
|
})
|
|
269
398
|
})
|
|
270
399
|
|
|
400
|
+
describe("OpaqueFacade", () => {
|
|
401
|
+
it("keeps OpaqueFacade usable with struct schemas", () => {
|
|
402
|
+
const decoded = S.decodeSync(StructFacadeModel)({ value: "hello" })
|
|
403
|
+
|
|
404
|
+
expect(decoded).toStrictEqual({ value: "hello" })
|
|
405
|
+
expect(S.encodeSync(StructFacadeModel)(decoded)).toStrictEqual({ value: "hello" })
|
|
406
|
+
expect(S.is(StructFacadeModel)(decoded)).toBe(true)
|
|
407
|
+
expect(S.is(StructFacadeModel)({ value: 1 })).toBe(false)
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
it("keeps encoded-key struct facades on the struct path", () => {
|
|
411
|
+
const decoded = S.decodeSync(RenamedStructFacadeModel)({ external_name: "fran" })
|
|
412
|
+
|
|
413
|
+
expect(decoded).toStrictEqual({ internalName: "fran" })
|
|
414
|
+
expect(decoded).not.toBeInstanceOf(RenamedStructFacadeModel)
|
|
415
|
+
expect(S.encodeSync(RenamedStructFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" })
|
|
416
|
+
expect(S.is(RenamedStructFacadeModel)(decoded)).toBe(true)
|
|
417
|
+
expect(S.is(RenamedStructFacadeModel)({ external_name: "fran" })).toBe(false)
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it("keeps encoded-key opaque struct facades on the struct path", () => {
|
|
421
|
+
const decoded = S.decodeSync(RenamedOpaqueStructFacadeModel)({ external_name: "fran" })
|
|
422
|
+
|
|
423
|
+
expect(decoded).toStrictEqual({ internalName: "fran" })
|
|
424
|
+
expect(decoded).not.toBeInstanceOf(RenamedOpaqueStructFacadeModel)
|
|
425
|
+
expect(S.encodeSync(RenamedOpaqueStructFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" })
|
|
426
|
+
expect(S.is(RenamedOpaqueStructFacadeModel)(decoded)).toBe(true)
|
|
427
|
+
expect(S.is(RenamedOpaqueStructFacadeModel)({ external_name: "fran" })).toBe(false)
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
it("decodes through the public facade class", () => {
|
|
431
|
+
const decoded = S.decodeSync(FacadeModel)({ value: "hello" })
|
|
432
|
+
const decodedEffect = Effect.runSync(S.decodeUnknownEffect(FacadeModel)({ value: "effect" }))
|
|
433
|
+
|
|
434
|
+
expect(decoded).toBeInstanceOf(FacadeModel)
|
|
435
|
+
expect(decodedEffect).toBeInstanceOf(FacadeModel)
|
|
436
|
+
expect(decoded).toBeInstanceOf(_FacadeModel)
|
|
437
|
+
expect(decoded.value).toBe("hello")
|
|
438
|
+
expect(decoded.upperValue).toBe("HELLO")
|
|
439
|
+
expect(decoded.decoratedValue).toBe("facade:hello")
|
|
440
|
+
expect(decoded.label()).toBe("facade:hello")
|
|
441
|
+
expect(FacadeModel.describe(decoded)).toBe("value:hello")
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it("validates facade instances with S.is", () => {
|
|
445
|
+
const instance = new FacadeModel({ value: "hello" })
|
|
446
|
+
|
|
447
|
+
expect(instance).toBeInstanceOf(FacadeModel)
|
|
448
|
+
expect(instance.upperValue).toBe("HELLO")
|
|
449
|
+
expect(instance.label()).toBe("facade:hello")
|
|
450
|
+
expect(S.is(FacadeModel)(instance)).toBe(true)
|
|
451
|
+
expect(S.is(FacadeModel)(S.decodeSync(FacadeModel)({ value: "world" }))).toBe(true)
|
|
452
|
+
expect(S.is(FacadeModel)({ value: "plain" })).toBe(false)
|
|
453
|
+
expect(S.is(FacadeModel)({ value: 1 })).toBe(false)
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
it("decodes renamed keys through the public facade class", () => {
|
|
457
|
+
const decoded = S.decodeSync(RenamedFacadeModel)({ external_name: "fran" })
|
|
458
|
+
|
|
459
|
+
expect(decoded).toBeInstanceOf(RenamedFacadeModel)
|
|
460
|
+
expect(decoded).toBeInstanceOf(_RenamedFacadeModel)
|
|
461
|
+
expect(Object.getPrototypeOf(decoded)).toBe(RenamedFacadeModel.prototype)
|
|
462
|
+
expect(decoded.internalName).toBe("fran")
|
|
463
|
+
expect(decoded.upperName).toBe("FRAN")
|
|
464
|
+
expect(decoded.decoratedName).toBe("facade:fran")
|
|
465
|
+
expect(S.encodeSync(RenamedFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" })
|
|
466
|
+
expect(S.is(RenamedFacadeModel)(decoded)).toBe(true)
|
|
467
|
+
expect(S.is(RenamedFacadeModel)({ internalName: "plain" })).toBe(false)
|
|
468
|
+
expect(S.is(RenamedFacadeModel)({ external_name: "plain" })).toBe(false)
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
it("keeps generated struct facades decodable with public statics", () => {
|
|
472
|
+
const input: RequestContext.Encoded = {
|
|
473
|
+
span: { traceId: "trace", spanId: "span", sampled: true },
|
|
474
|
+
name: "operation",
|
|
475
|
+
locale: "en",
|
|
476
|
+
namespace: "tenant"
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const decoded = S.decodeSync(RequestContext)(input)
|
|
480
|
+
|
|
481
|
+
expect(decoded).not.toBeInstanceOf(RequestContext)
|
|
482
|
+
expect(S.is(RequestContext)(decoded)).toBe(true)
|
|
483
|
+
expect(RequestContext.toMonitoring(decoded)).toStrictEqual({ operationName: "operation", locale: "en" })
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
it("keeps error facades class-only", () => {
|
|
487
|
+
const makeStructErrorFacade = () =>
|
|
488
|
+
AppSchema.OpaqueErrorFacadeClass<
|
|
489
|
+
{ readonly value: string },
|
|
490
|
+
{ readonly value: string },
|
|
491
|
+
{ readonly value: string },
|
|
492
|
+
never,
|
|
493
|
+
never
|
|
494
|
+
>()(
|
|
495
|
+
// @ts-expect-error OpaqueErrorFacadeClass requires a class schema.
|
|
496
|
+
AppSchema.Struct({ value: S.String })
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
expect(makeStructErrorFacade).toThrow("OpaqueErrorFacadeClass requires a class schema")
|
|
500
|
+
})
|
|
501
|
+
})
|
|
502
|
+
|
|
271
503
|
describe("Struct.copy", () => {
|
|
272
504
|
it("creates a new value with updated fields", () => {
|
|
273
505
|
const A = AppSchema.Struct({ a: S.String, b: S.Number })
|