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.
@@ -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 })