effect-app 4.0.0-beta.10 → 4.0.0-beta.100

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.
Files changed (141) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/dist/Config/SecretURL.js +2 -2
  3. package/dist/Config.d.ts +7 -0
  4. package/dist/Config.d.ts.map +1 -0
  5. package/dist/Config.js +6 -0
  6. package/dist/ConfigProvider.d.ts +39 -0
  7. package/dist/ConfigProvider.d.ts.map +1 -0
  8. package/dist/ConfigProvider.js +42 -0
  9. package/dist/{ServiceMap.d.ts → Context.d.ts} +14 -18
  10. package/dist/Context.d.ts.map +1 -0
  11. package/dist/Context.js +66 -0
  12. package/dist/Effect.d.ts +8 -7
  13. package/dist/Effect.d.ts.map +1 -1
  14. package/dist/Effect.js +3 -2
  15. package/dist/Layer.d.ts +5 -4
  16. package/dist/Layer.d.ts.map +1 -1
  17. package/dist/Layer.js +1 -1
  18. package/dist/Operations.d.ts +62 -25
  19. package/dist/Operations.d.ts.map +1 -1
  20. package/dist/Pure.d.ts +2 -2
  21. package/dist/Pure.d.ts.map +1 -1
  22. package/dist/Pure.js +13 -13
  23. package/dist/Schema/Class.d.ts +43 -10
  24. package/dist/Schema/Class.d.ts.map +1 -1
  25. package/dist/Schema/Class.js +89 -16
  26. package/dist/Schema/SpecialJsonSchema.d.ts +21 -0
  27. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
  28. package/dist/Schema/SpecialJsonSchema.js +59 -0
  29. package/dist/Schema/SpecialOpenApi.d.ts +32 -0
  30. package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
  31. package/dist/Schema/SpecialOpenApi.js +123 -0
  32. package/dist/Schema/brand.d.ts +5 -1
  33. package/dist/Schema/brand.d.ts.map +1 -1
  34. package/dist/Schema/brand.js +1 -1
  35. package/dist/Schema/email.d.ts.map +1 -1
  36. package/dist/Schema/email.js +9 -4
  37. package/dist/Schema/ext.d.ts +103 -46
  38. package/dist/Schema/ext.d.ts.map +1 -1
  39. package/dist/Schema/ext.js +110 -51
  40. package/dist/Schema/moreStrings.d.ts +19 -7
  41. package/dist/Schema/moreStrings.d.ts.map +1 -1
  42. package/dist/Schema/moreStrings.js +14 -9
  43. package/dist/Schema/numbers.d.ts +11 -11
  44. package/dist/Schema/numbers.d.ts.map +1 -1
  45. package/dist/Schema/numbers.js +10 -9
  46. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  47. package/dist/Schema/phoneNumber.js +8 -3
  48. package/dist/Schema/strings.d.ts +4 -4
  49. package/dist/Schema/strings.d.ts.map +1 -1
  50. package/dist/Schema.d.ts +22 -55
  51. package/dist/Schema.d.ts.map +1 -1
  52. package/dist/Schema.js +43 -63
  53. package/dist/client/apiClientFactory.d.ts +11 -28
  54. package/dist/client/apiClientFactory.d.ts.map +1 -1
  55. package/dist/client/apiClientFactory.js +14 -15
  56. package/dist/client/clientFor.d.ts +6 -5
  57. package/dist/client/clientFor.d.ts.map +1 -1
  58. package/dist/client/errors.d.ts +18 -9
  59. package/dist/client/errors.d.ts.map +1 -1
  60. package/dist/client/errors.js +35 -10
  61. package/dist/client/makeClient.d.ts +20 -15
  62. package/dist/client/makeClient.d.ts.map +1 -1
  63. package/dist/client/makeClient.js +32 -23
  64. package/dist/http/Request.d.ts.map +1 -1
  65. package/dist/http/Request.js +5 -5
  66. package/dist/ids.d.ts +2 -2
  67. package/dist/ids.d.ts.map +1 -1
  68. package/dist/ids.js +3 -2
  69. package/dist/index.d.ts +3 -8
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +4 -9
  72. package/dist/middleware.d.ts +2 -2
  73. package/dist/middleware.d.ts.map +1 -1
  74. package/dist/middleware.js +3 -3
  75. package/dist/rpc/MiddlewareMaker.d.ts +4 -3
  76. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  77. package/dist/rpc/MiddlewareMaker.js +7 -6
  78. package/dist/rpc/RpcContextMap.d.ts +2 -2
  79. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  80. package/dist/rpc/RpcContextMap.js +4 -4
  81. package/dist/rpc/RpcMiddleware.d.ts +4 -3
  82. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  83. package/dist/rpc/RpcMiddleware.js +1 -1
  84. package/dist/utils/gen.d.ts +1 -1
  85. package/dist/utils/gen.d.ts.map +1 -1
  86. package/dist/utils/logger.d.ts +2 -2
  87. package/dist/utils/logger.d.ts.map +1 -1
  88. package/dist/utils/logger.js +3 -3
  89. package/dist/utils.d.ts +18 -0
  90. package/dist/utils.d.ts.map +1 -1
  91. package/dist/utils.js +22 -2
  92. package/package.json +29 -17
  93. package/src/Config/SecretURL.ts +1 -1
  94. package/src/Config.ts +14 -0
  95. package/src/ConfigProvider.ts +48 -0
  96. package/src/{ServiceMap.ts → Context.ts} +51 -60
  97. package/src/Effect.ts +11 -9
  98. package/src/Layer.ts +5 -4
  99. package/src/Pure.ts +17 -18
  100. package/src/Schema/Class.ts +118 -30
  101. package/src/Schema/SpecialJsonSchema.ts +69 -0
  102. package/src/Schema/SpecialOpenApi.ts +130 -0
  103. package/src/Schema/brand.ts +10 -3
  104. package/src/Schema/email.ts +10 -2
  105. package/src/Schema/ext.ts +185 -82
  106. package/src/Schema/moreStrings.ts +21 -11
  107. package/src/Schema/numbers.ts +9 -8
  108. package/src/Schema/phoneNumber.ts +8 -1
  109. package/src/Schema.ts +79 -103
  110. package/src/client/apiClientFactory.ts +24 -29
  111. package/src/client/clientFor.ts +6 -1
  112. package/src/client/errors.ts +34 -9
  113. package/src/client/makeClient.ts +121 -61
  114. package/src/http/Request.ts +7 -4
  115. package/src/ids.ts +2 -1
  116. package/src/index.ts +3 -11
  117. package/src/middleware.ts +2 -2
  118. package/src/rpc/MiddlewareMaker.ts +8 -7
  119. package/src/rpc/RpcContextMap.ts +6 -5
  120. package/src/rpc/RpcMiddleware.ts +5 -4
  121. package/src/utils/gen.ts +1 -1
  122. package/src/utils/logger.ts +2 -2
  123. package/src/utils.ts +24 -1
  124. package/test/dist/moreStrings.test.d.ts.map +1 -0
  125. package/test/dist/rpc.test.d.ts.map +1 -1
  126. package/test/dist/secretURL.test.d.ts.map +1 -0
  127. package/test/dist/special.test.d.ts.map +1 -0
  128. package/test/moreStrings.test.ts +17 -0
  129. package/test/rpc.test.ts +28 -6
  130. package/test/schema.test.ts +397 -4
  131. package/test/secretURL.test.ts +157 -0
  132. package/test/special.test.ts +729 -0
  133. package/test/utils.test.ts +2 -2
  134. package/tsconfig.base.json +0 -1
  135. package/tsconfig.json +0 -1
  136. package/dist/ServiceMap.d.ts.map +0 -1
  137. package/dist/ServiceMap.js +0 -91
  138. package/dist/Struct.d.ts +0 -44
  139. package/dist/Struct.d.ts.map +0 -1
  140. package/dist/Struct.js +0 -29
  141. package/src/Struct.ts +0 -54
package/src/Schema.ts CHANGED
@@ -1,22 +1,19 @@
1
- import { Array, Option, pipe, SchemaAST, type Tracer } from "effect"
1
+ import { SchemaAST, type Tracer } from "effect"
2
2
  import * as S from "effect/Schema"
3
3
  import type { NonEmptyReadonlyArray } from "./Array.js"
4
4
  import { fakerArb } from "./faker.js"
5
- import { Email as EmailT } from "./Schema/email.js"
5
+ import { Email as EmailT, type Email as EmailType } from "./Schema/email.js"
6
6
  import { withDefaultMake } from "./Schema/ext.js"
7
- import { PhoneNumber as PhoneNumberT } from "./Schema/phoneNumber.js"
8
- import type { AST } from "./Schema/schema.js"
7
+ import { PhoneNumber as PhoneNumberT, type PhoneNumber as PhoneNumberType } from "./Schema/phoneNumber.js"
9
8
  import { extendM } from "./utils.js"
10
9
 
11
10
  export * from "effect/Schema"
12
- // v4: TaggedError renamed to TaggedErrorClass
13
- export { TaggedErrorClass as TaggedError } from "effect/Schema"
14
11
 
15
12
  export * from "./Schema/Class.js"
16
13
  export { Class, TaggedClass } from "./Schema/Class.js"
17
14
 
18
15
  export { fromBrand, nominal } from "./Schema/brand.js"
19
- export { Array, Boolean, Date, Literal, Map, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.js"
16
+ export { Array, Boolean, Date, DateFromString, DateValid, Finite, Literals, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.js"
20
17
  export { Int, NonNegativeInt } from "./Schema/numbers.js"
21
18
 
22
19
  export * from "./Schema/email.js"
@@ -25,6 +22,8 @@ export * from "./Schema/moreStrings.js"
25
22
  export * from "./Schema/numbers.js"
26
23
  export * from "./Schema/phoneNumber.js"
27
24
  export * from "./Schema/schema.js"
25
+ export * from "./Schema/SpecialJsonSchema.js"
26
+ export * from "./Schema/SpecialOpenApi.js"
28
27
  export * from "./Schema/strings.js"
29
28
  export { NonEmptyString } from "./Schema/strings.js"
30
29
 
@@ -40,127 +39,104 @@ export interface WithOptionalSpan {
40
39
  [SpanId]?: Tracer.Span
41
40
  }
42
41
 
42
+ const makeEmail = S.decodeSync(EmailT as any) as (value: string) => EmailType
43
+ const makePhoneNumber = S.decodeSync(PhoneNumberT as any) as (value: string) => PhoneNumberType
44
+
43
45
  export const Email = EmailT
44
46
  .pipe(
45
47
  S.annotate({
46
48
  // eslint-disable-next-line @typescript-eslint/unbound-method
47
- arbitrary: (): any => (fc: any) => fakerArb((faker) => faker.internet.exampleEmail)(fc).map(Email)
49
+ toArbitrary: () => (fc) => fakerArb((faker) => faker.internet.exampleEmail)(fc).map(makeEmail)
48
50
  }),
49
51
  withDefaultMake
50
52
  )
51
53
 
52
- export type Email = EmailT
54
+ export type Email = EmailType
53
55
 
54
56
  export const PhoneNumber = PhoneNumberT
55
57
  .pipe(
56
58
  S.annotate({
57
- arbitrary: (): any => (fc: any) =>
59
+ toArbitrary: () => (fc) =>
58
60
  // eslint-disable-next-line @typescript-eslint/unbound-method
59
- fakerArb((faker) => faker.phone.number)(fc).map(PhoneNumber)
61
+ fakerArb((faker) => faker.phone.number)(fc).map(makePhoneNumber)
60
62
  }),
61
63
  withDefaultMake
62
64
  )
63
65
 
64
- export const makeIs = <A extends { _tag: string }, I, R>(
65
- schema: S.Codec<A, I, R>
66
- ) => {
67
- // In v4, transformations are stored as encoding on nodes, not as wrapper nodes.
68
- // Union member ASTs are directly Objects (TypeLiteral equivalent).
69
- if (SchemaAST.isUnion(schema.ast)) {
70
- return schema.ast.types.reduce((acc: any, t: AST.AST) => {
71
- if (!SchemaAST.isObjects(t)) return acc
72
- const tag = Array.findFirst(t.propertySignatures, (_: any) => {
73
- if (_.name === "_tag" && SchemaAST.isLiteral(_.type)) {
74
- return Option.some(_.type)
75
- }
76
- return Option.none()
77
- })
78
- const ast = Option.getOrUndefined(tag)
79
- if (!ast) {
80
- return acc
81
- }
82
- return {
83
- ...acc,
84
- [String((ast as SchemaAST.Literal).literal)]: (x: { _tag: string }) =>
85
- x._tag === (ast as SchemaAST.Literal).literal
86
- }
87
- }, {} as Is<A>)
88
- }
89
- throw new Error("Unsupported")
90
- }
66
+ export type PhoneNumber = PhoneNumberType
91
67
 
92
- export const makeIsAnyOf = <A extends { _tag: string }, I, R>(
93
- schema: S.Codec<A, I, R>
94
- ): IsAny<A> => {
95
- if (SchemaAST.isUnion(schema.ast)) {
96
- return <Keys extends A["_tag"][]>(...keys: Keys) => (a: A): a is ExtractUnion<A, ElemType<Keys>> =>
97
- keys.includes(a._tag)
98
- }
99
- throw new Error("Unsupported")
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")
100
75
  }
101
76
 
102
- export type ExtractUnion<A extends { _tag: string }, Tags extends A["_tag"]> = Extract<A, Record<"_tag", Tags>>
103
- export type Is<A extends { _tag: string }> = { [K in A as K["_tag"]]: (a: A) => a is K }
104
- export type ElemType<A> = A extends Array<infer E> ? E : never
105
- export interface IsAny<A extends { _tag: string }> {
106
- <Keys extends A["_tag"][]>(...keys: Keys): (a: A) => a is ExtractUnion<A, ElemType<Keys>>
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 []
100
+ }
107
101
  }
108
102
 
109
- export const taggedUnionMap = <
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
- Members extends readonly (S.Top & { fields: { _tag: S.tag<string> } })[]
112
- >(
113
- self: Members
114
- ) =>
115
- self.reduce((acc, key) => {
116
- // TODO: v4 migration — PropertySignatureDeclaration removed, need v4 AST traversal
117
- const ast = key.fields._tag.ast as any
118
- const tag = ((ast.type ?? ast) as SchemaAST.Literal).literal as string // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
119
- acc[tag] = key as any
120
- return acc
121
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
- }, {} as any)
123
-
124
103
  export const tags = <
125
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
- Members extends NonEmptyReadonlyArray<(S.Top & { fields: { _tag: S.tag<string> } })>
104
+ Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })>
127
105
  >(
128
106
  self: Members
129
107
  ) =>
130
- S.Literals(self.map((key) => {
131
- // TODO: v4 migration — PropertySignatureDeclaration removed, need v4 AST traversal
132
- const ast = key.fields._tag.ast as any
133
- const tag = ((ast.type ?? ast) as SchemaAST.Literal).literal
134
- return tag
135
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
- })) as any
137
-
138
- export const ExtendTaggedUnion = <A extends { _tag: string }, I, R>(
139
- schema: S.Codec<A, I, R>
140
- ) =>
141
- extendM(
142
- schema,
143
- (_) => ({
144
- is: S.is(schema as any),
145
- isA: makeIs(_ as any),
146
- isAnyOf: makeIsAnyOf(_ as any) /*, map: taggedUnionMap(a) */
147
- })
148
- )
108
+ S.Literals(
109
+ self.map(getTagFromAST) as {
110
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
111
+ }
112
+ ) as S.Literals<
113
+ {
114
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
115
+ }
116
+ >
117
+
118
+ type TaggedUnionMembers = NonEmptyReadonlyArray<
119
+ S.Top & { readonly Type: { readonly _tag: string } }
120
+ >
121
+
122
+ type TaggedUnionTags<Members extends TaggedUnionMembers> = S.Literals<
123
+ {
124
+ [Index in keyof Members]: Members[Index]["Type"]["_tag"]
125
+ }
126
+ >
149
127
 
150
- export const TaggedUnion = <
151
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
- Members extends readonly (S.Top & { fields: { _tag: S.tag<any> } })[]
153
- >(...a: Members) =>
154
- pipe(
155
- S.Union(a),
156
- (_) =>
157
- extendM(_, (_) => ({
158
- is: S.is(_ as any),
159
- isA: makeIs(_ as any),
160
- isAnyOf: makeIsAnyOf(_ as any),
161
- tagMap: taggedUnionMap(a),
162
- tags: tags(a as any)
163
- }))
164
- )
128
+ type TaggedUnionWithTags<Members extends TaggedUnionMembers> = S.toTaggedUnion<"_tag", Members> & {
129
+ readonly tags: TaggedUnionTags<Members>
130
+ }
131
+
132
+ const extendTaggedUnionWithTags = <Members extends TaggedUnionMembers>(
133
+ schema: S.Union<Members>
134
+ ): TaggedUnionWithTags<Members> => extendM(schema.pipe(S.toTaggedUnion("_tag")), () => ({ tags: tags(schema.members) }))
165
135
 
166
- export type PhoneNumber = PhoneNumberT
136
+ export const ExtendTaggedUnion = <Members extends TaggedUnionMembers>(
137
+ schema: S.Union<Members>
138
+ ): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(schema)
139
+
140
+ export const TaggedUnion = <
141
+ Members extends TaggedUnionMembers
142
+ >(...a: Members): TaggedUnionWithTags<Members> => extendTaggedUnionWithTags(S.Union(a))
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import * as Config from "effect/Config"
3
2
  import { flow } from "effect/Function"
4
3
  import * as Layer from "effect/Layer"
5
4
  import * as ManagedRuntime from "effect/ManagedRuntime"
@@ -7,13 +6,14 @@ import * as Predicate from "effect/Predicate"
7
6
  import * as Schema from "effect/Schema"
8
7
  import * as Struct from "effect/Struct"
9
8
  import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/unstable/rpc"
9
+ import * as Config from "../Config.js"
10
+ import * as Context from "../Context.js"
10
11
  import * as Effect from "../Effect.js"
11
12
  import { HttpClient, HttpClientRequest } from "../http.js"
12
13
  import * as Option from "../Option.js"
13
14
  import type * as S from "../Schema.js"
14
- import * as ServiceMap from "../ServiceMap.js"
15
15
  import { typedKeysOf, typedValuesOf } from "../utils.js"
16
- import type { Client, ClientForOptions, Requests, RequestsAny } from "./clientFor.js"
16
+ import type { Client, ClientForOptions, ExtractModuleName, RequestsAny } from "./clientFor.js"
17
17
 
18
18
  export interface ApiConfig {
19
19
  url: string
@@ -37,10 +37,12 @@ export type Req = S.Top & {
37
37
  success: S.Top
38
38
  error: S.Top
39
39
  config?: Record<string, any>
40
+ readonly id: string
41
+ readonly moduleName: string
40
42
  readonly "~decodingServices"?: unknown
41
43
  }
42
44
 
43
- class RequestName extends ServiceMap.Reference("RequestName", {
45
+ class RequestName extends Context.Reference("RequestName", {
44
46
  defaultValue: () => ({ requestName: "Unspecified", moduleName: "Error" })
45
47
  }) {}
46
48
 
@@ -84,7 +86,7 @@ type RpcHandlers<M extends RequestsAny> = {
84
86
  [K in keyof M]: Rpc.Rpc<M[K]["_tag"], M[K], M[K]["success"], M[K]["error"]>
85
87
  }
86
88
 
87
- const getFiltered = <M extends Requests>(resource: M) => {
89
+ const getFiltered = <M extends RequestsAny>(resource: M) => {
88
90
  type Filtered = {
89
91
  [K in keyof M as M[K] extends Req ? K : never]: M[K] extends Req ? M[K] : never
90
92
  }
@@ -102,13 +104,13 @@ const getFiltered = <M extends Requests>(resource: M) => {
102
104
  return filtered as unknown as Filtered
103
105
  }
104
106
 
105
- export const getMeta = <M extends Requests>(resource: M) => {
106
- const meta = (resource as any).meta as { moduleName: string }
107
- if (!meta) throw new Error("No meta defined in Resource!")
108
- return meta as M["meta"]
107
+ export const getMeta = <M extends RequestsAny>(resource: M): { moduleName: ExtractModuleName<M> } => {
108
+ const first = typedValuesOf(getFiltered(resource))[0]
109
+ if (first && "moduleName" in first) return { moduleName: first.moduleName } as any
110
+ throw new Error("No moduleName on requests!")
109
111
  }
110
112
 
111
- export const makeRpcGroupFromRequestsAndModuleName = <M extends Requests, const ModuleName extends string>(
113
+ export const makeRpcGroupFromRequestsAndModuleName = <M extends RequestsAny, const ModuleName extends string>(
112
114
  resource: M,
113
115
  moduleName: ModuleName
114
116
  ) => {
@@ -126,20 +128,13 @@ export const makeRpcGroupFromRequestsAndModuleName = <M extends Requests, const
126
128
  return rpcs
127
129
  }
128
130
 
129
- export const makeRpcGroup = <
130
- M extends Requests,
131
- const ModuleName extends string
132
- >(
133
- resource: M & { meta: { moduleName: ModuleName } }
134
- ) => makeRpcGroupFromRequestsAndModuleName(resource, resource.meta.moduleName)
135
-
136
- const makeRpcTag = <M extends Requests>(resource: M) => {
131
+ const makeRpcTag = <M extends RequestsAny>(resource: M) => {
137
132
  const meta = getMeta(resource)
138
133
  const rpcs = makeRpcGroupFromRequestsAndModuleName(resource, meta.moduleName)
139
134
 
140
135
  // Use Object.assign instead of class extension to avoid TS2509 with complex generic return types.
141
136
  // The first type arg is `any` because this is a dynamically created tag — its identity is the string key.
142
- const TheClient = ServiceMap.Opaque<
137
+ const TheClient = Context.Opaque<
143
138
  any,
144
139
  RpcClient.RpcClient<RpcGroup.Rpcs<typeof rpcs>>
145
140
  >()(`RpcClient.${meta.moduleName}`)
@@ -153,8 +148,8 @@ const makeRpcTag = <M extends Requests>(resource: M) => {
153
148
 
154
149
  const makeApiClientFactory = Effect
155
150
  .gen(function*() {
156
- const ctx = yield* Effect.services<RpcSerialization.RpcSerialization | HttpClient.HttpClient>()
157
- const makeClientFor = <M extends Requests>(
151
+ const ctx = yield* Effect.context<RpcSerialization.RpcSerialization | HttpClient.HttpClient>()
152
+ const makeClientFor = <M extends RequestsAny>(
158
153
  resource: M,
159
154
  requestLevelLayers = Layer.empty,
160
155
  options?: ClientForOptions
@@ -176,7 +171,7 @@ const makeApiClientFactory = Effect
176
171
  url: "" // why not here set meta.moduleName as root?
177
172
  })
178
173
  .pipe(
179
- Layer.provideMerge(Layer.succeedServices(ctx))
174
+ Layer.provideMerge(Layer.succeedContext(ctx))
180
175
  )
181
176
  )
182
177
  )
@@ -212,7 +207,7 @@ const makeApiClientFactory = Effect
212
207
  // @ts-expect-error doc
213
208
  prev[cur] = Object.keys(fields).length === 0
214
209
  ? {
215
- handler: mr.servicesEffect.pipe(
210
+ handler: mr.contextEffect.pipe(
216
211
  Effect.flatMap((svcs) =>
217
212
  TheClient
218
213
  .use((client) => (client as any)[requestAttr]!(new Request()) as Effect.Effect<any, any, never>)
@@ -226,7 +221,7 @@ const makeApiClientFactory = Effect
226
221
  }
227
222
  : {
228
223
  handler: (req: any) =>
229
- mr.servicesEffect.pipe(
224
+ mr.contextEffect.pipe(
230
225
  Effect.flatMap((svcs) =>
231
226
  TheClient
232
227
  .use((client) =>
@@ -243,7 +238,7 @@ const makeApiClientFactory = Effect
243
238
  }
244
239
 
245
240
  return prev
246
- }, {} as Client<M, M["meta"]["moduleName"]>)
241
+ }, {} as Client<M, ExtractModuleName<M>>)
247
242
  }
248
243
  })
249
244
 
@@ -259,9 +254,9 @@ const makeApiClientFactory = Effect
259
254
  cacheL.set(requestLevelLayers, cache)
260
255
  }
261
256
 
262
- return <M extends Requests>(
257
+ return <M extends RequestsAny>(
263
258
  models: M
264
- ): Effect.Effect<Client<M, M["meta"]["moduleName"]>> =>
259
+ ): Effect.Effect<Client<M, ExtractModuleName<M>>> =>
265
260
  Effect.gen(function*() {
266
261
  const found = cache.get(models)
267
262
  if (found) {
@@ -281,7 +276,7 @@ const makeApiClientFactory = Effect
281
276
  * Used to create clients for resource modules.
282
277
  */
283
278
  export class ApiClientFactory
284
- extends ServiceMap.Opaque<ApiClientFactory, Effect.Success<typeof makeApiClientFactory>>()("ApiClientFactory")
279
+ extends Context.Opaque<ApiClientFactory, Effect.Success<typeof makeApiClientFactory>>()("ApiClientFactory")
285
280
  {
286
281
  static readonly layer = (config: ApiConfig) =>
287
282
  ApiClientFactory.toLayer(makeApiClientFactory).pipe(Layer.provide(RpcSerializationLayer(config)))
@@ -294,7 +289,7 @@ export class ApiClientFactory
294
289
 
295
290
  static readonly makeFor =
296
291
  (requestLevelLayers: Layer.Layer<never, never, never>, options?: ClientForOptions) =>
297
- <M extends Requests>(
292
+ <M extends RequestsAny>(
298
293
  resource: M
299
294
  ) =>
300
295
  ApiClientFactory.use((apiClientFactory) => {
@@ -48,9 +48,14 @@ export function makePathWithBody(
48
48
  return path.build(pars, { ignoreSearch: true, ignoreConstraints: true })
49
49
  }
50
50
 
51
- export type Requests<ModuleName extends string = string> = { meta: { moduleName: ModuleName } } & RequestsAny
51
+ export type Requests = RequestsAny
52
52
  export type RequestsAny = Record<string, any>
53
53
 
54
+ export type ExtractModuleName<M extends RequestsAny> =
55
+ { [K in keyof M]: M[K] extends { moduleName: infer N extends string } ? N : never }[keyof M] extends
56
+ infer R extends string ? R
57
+ : string
58
+
54
59
  export type Client<M extends RequestsAny, ModuleName extends string> = RequestHandlers<
55
60
  never,
56
61
  never,
@@ -1,5 +1,5 @@
1
1
  /** @effect-diagnostics overriddenSchemaConstructor:skip-file */
2
- import { TaggedError } from "effect-app/Schema"
2
+ import { TaggedErrorClass } from "effect-app/Schema"
3
3
  import * as Cause from "effect/Cause"
4
4
  import * as S from "../Schema.js"
5
5
 
@@ -21,7 +21,7 @@ export const tryToJson = (error: { toJSON(): unknown; toString(): string }) => {
21
21
 
22
22
  // eslint-disable-next-line unused-imports/no-unused-vars
23
23
  // @ts-expect-error type not used
24
- export class NotFoundError<ItemType = string> extends TaggedError<NotFoundError<ItemType>>()("NotFoundError", {
24
+ export class NotFoundError<ItemType = string> extends TaggedErrorClass<NotFoundError<ItemType>>()("NotFoundError", {
25
25
  type: S.String,
26
26
  id: S.Unknown
27
27
  }) {
@@ -34,12 +34,15 @@ export class NotFoundError<ItemType = string> extends TaggedError<NotFoundError<
34
34
  override get message() {
35
35
  return `Didn't find ${(this as any).type}#${JSON.stringify((this as any).id)}`
36
36
  }
37
+ override toString() {
38
+ return `NotFoundError: ${this.message}`
39
+ }
37
40
  }
38
41
 
39
42
  const messageFallback = (messageOrObject?: string | { message: string }) =>
40
43
  typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject ?? "" }
41
44
 
42
- export class InvalidStateError extends TaggedError<InvalidStateError>()("InvalidStateError", {
45
+ export class InvalidStateError extends TaggedErrorClass<InvalidStateError>()("InvalidStateError", {
43
46
  message: S.String
44
47
  }) {
45
48
  constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
@@ -48,9 +51,12 @@ export class InvalidStateError extends TaggedError<InvalidStateError>()("Invalid
48
51
  disableValidation as any
49
52
  )
50
53
  }
54
+ override toString() {
55
+ return `InvalidStateError: ${this.message}`
56
+ }
51
57
  }
52
58
 
53
- export class ServiceUnavailableError extends TaggedError<ServiceUnavailableError>()("ServiceUnavailableError", {
59
+ export class ServiceUnavailableError extends TaggedErrorClass<ServiceUnavailableError>()("ServiceUnavailableError", {
54
60
  message: S.String
55
61
  }) {
56
62
  constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
@@ -59,9 +65,12 @@ export class ServiceUnavailableError extends TaggedError<ServiceUnavailableError
59
65
  disableValidation as any
60
66
  )
61
67
  }
68
+ override toString() {
69
+ return `ServiceUnavailableError: ${this.message}`
70
+ }
62
71
  }
63
72
 
64
- export class ValidationError extends TaggedError<ValidationError>()("ValidationError", {
73
+ export class ValidationError extends TaggedErrorClass<ValidationError>()("ValidationError", {
65
74
  errors: S.Array(S.Unknown)
66
75
  }) {
67
76
  constructor(
@@ -73,33 +82,45 @@ export class ValidationError extends TaggedError<ValidationError>()("ValidationE
73
82
  override get message() {
74
83
  return `Validation failed: ${(this as any).errors.map((e: any) => JSON.stringify(e, undefined, 2)).join(",\n")}`
75
84
  }
85
+ override toString() {
86
+ return `ValidationError: ${this.message}`
87
+ }
76
88
  }
77
89
 
78
- export class NotLoggedInError extends TaggedError<NotLoggedInError>()("NotLoggedInError", {
90
+ export class NotLoggedInError extends TaggedErrorClass<NotLoggedInError>()("NotLoggedInError", {
79
91
  message: S.String
80
92
  }) {
81
93
  constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
82
94
  super(messageFallback(messageOrObject) as any, disableValidation as any)
83
95
  }
96
+ override toString() {
97
+ return `NotLoggedInError: ${this.message}`
98
+ }
84
99
  }
85
100
 
86
101
  /**
87
102
  * The user carries a valid Userprofile, but there is a problem with the login none the less.
88
103
  */
89
- export class LoginError extends TaggedError<LoginError>()("NotLoggedInError", {
104
+ export class LoginError extends TaggedErrorClass<LoginError>()("NotLoggedInError", {
90
105
  message: S.String
91
106
  }) {
92
107
  constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
93
108
  super(messageFallback(messageOrObject) as any, disableValidation as any)
94
109
  }
110
+ override toString() {
111
+ return `LoginError: ${this.message}`
112
+ }
95
113
  }
96
114
 
97
- export class UnauthorizedError extends TaggedError<UnauthorizedError>()("UnauthorizedError", {
115
+ export class UnauthorizedError extends TaggedErrorClass<UnauthorizedError>()("UnauthorizedError", {
98
116
  message: S.String
99
117
  }) {
100
118
  constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) {
101
119
  super(messageFallback(messageOrObject) as any, disableValidation as any)
102
120
  }
121
+ override toString() {
122
+ return `UnauthorizedError: ${this.message}`
123
+ }
103
124
  }
104
125
 
105
126
  type OptimisticConcurrencyDetails = {
@@ -110,7 +131,7 @@ type OptimisticConcurrencyDetails = {
110
131
  readonly found?: string | undefined
111
132
  }
112
133
 
113
- export class OptimisticConcurrencyException extends TaggedError<OptimisticConcurrencyException>()(
134
+ export class OptimisticConcurrencyException extends TaggedErrorClass<OptimisticConcurrencyException>()(
114
135
  "OptimisticConcurrencyException",
115
136
  { message: S.String }
116
137
  ) {
@@ -130,6 +151,9 @@ export class OptimisticConcurrencyException extends TaggedError<OptimisticConcur
130
151
  this.details = args
131
152
  }
132
153
  }
154
+ override toString() {
155
+ return `OptimisticConcurrencyException: ${this.message}`
156
+ }
133
157
  }
134
158
 
135
159
  const MutationOnlyErrors = [
@@ -183,6 +207,7 @@ export class CauseException<E> extends Error {
183
207
  Error.stackTraceLimit = 0
184
208
  super()
185
209
  Error.stackTraceLimit = limit
210
+ this.cause = Cause.squash(originalCause)
186
211
  // v4: makeFiberFailure removed — use Cause.prettyErrors instead
187
212
  const errors = Cause.prettyErrors(originalCause)
188
213
  const first = errors[0]