effect-app 4.0.0-beta.8 → 4.0.0-beta.82

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 (142) hide show
  1. package/CHANGELOG.md +354 -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/Context.d.ts +40 -0
  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 +39 -1
  24. package/dist/Schema/Class.d.ts.map +1 -1
  25. package/dist/Schema/Class.js +89 -12
  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 +8 -5
  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 -64
  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 +18 -19
  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 +21 -16
  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 +3 -3
  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 +24 -5
  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} +57 -64
  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 +114 -16
  101. package/src/Schema/SpecialJsonSchema.ts +69 -0
  102. package/src/Schema/SpecialOpenApi.ts +130 -0
  103. package/src/Schema/brand.ts +13 -7
  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 +31 -35
  111. package/src/client/clientFor.ts +6 -1
  112. package/src/client/errors.ts +46 -12
  113. package/src/client/makeClient.ts +122 -62
  114. package/src/http/Request.ts +7 -4
  115. package/src/ids.ts +3 -2
  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 +26 -4
  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 +732 -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 +0 -44
  137. package/dist/ServiceMap.d.ts.map +0 -1
  138. package/dist/ServiceMap.js +0 -91
  139. package/dist/Struct.d.ts +0 -44
  140. package/dist/Struct.d.ts.map +0 -1
  141. package/dist/Struct.js +0 -29
  142. package/src/Struct.ts +0 -54
@@ -1,8 +1,9 @@
1
+ import { Effect } from "effect"
1
2
  import { extendM } from "effect-app/utils"
2
3
  import * as S from "effect/Schema"
3
4
  import type { Simplify } from "effect/Types"
4
5
  import { fromBrand, nominal } from "./brand.js"
5
- import { withDefaultConstructor, withDefaultMake } from "./ext.js"
6
+ import { withDefaultMake } from "./ext.js"
6
7
  import { type B } from "./schema.js"
7
8
 
8
9
  export interface PositiveIntBrand
@@ -14,7 +15,7 @@ export const PositiveInt = extendM(
14
15
  fromBrand(nominal<PositiveInt>(), { identifier: "PositiveInt", title: "PositiveInt", jsonSchema: {} }),
15
16
  withDefaultMake
16
17
  ),
17
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(1))) })
18
+ (s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) })
18
19
  )
19
20
  export type PositiveInt = number & PositiveIntBrand
20
21
 
@@ -29,20 +30,20 @@ export const NonNegativeInt = extendM(
29
30
  }),
30
31
  withDefaultMake
31
32
  ),
32
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(0))) })
33
+ (s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
33
34
  )
34
35
  export type NonNegativeInt = number & NonNegativeIntBrand
35
36
 
36
37
  export interface IntBrand extends Simplify<B.Brand<"Int">> {}
37
38
  export const Int = extendM(
38
39
  S.Int.pipe(fromBrand(nominal<Int>(), { identifier: "Int", title: "Int", jsonSchema: {} }), withDefaultMake),
39
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(0))) })
40
+ (s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
40
41
  )
41
42
  export type Int = number & IntBrand
42
43
 
43
44
  export interface PositiveNumberBrand extends Simplify<B.Brand<"PositiveNumber"> & NonNegativeNumberBrand> {}
44
45
  export const PositiveNumber = extendM(
45
- S.Number.pipe(
46
+ S.Finite.pipe(
46
47
  S.check(S.isGreaterThan(0)),
47
48
  fromBrand(nominal<PositiveNumber>(), {
48
49
  identifier: "PositiveNumber",
@@ -51,14 +52,14 @@ export const PositiveNumber = extendM(
51
52
  }),
52
53
  withDefaultMake
53
54
  ),
54
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(1))) })
55
+ (s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) })
55
56
  )
56
57
  export type PositiveNumber = number & PositiveNumberBrand
57
58
 
58
59
  export interface NonNegativeNumberBrand extends Simplify<B.Brand<"NonNegativeNumber">> {}
59
60
  export const NonNegativeNumber = extendM(
60
61
  S
61
- .Number
62
+ .Finite
62
63
  .pipe(
63
64
  S.check(S.isGreaterThanOrEqualTo(0)),
64
65
  fromBrand(nominal<NonNegativeNumber>(), {
@@ -68,7 +69,7 @@ export const NonNegativeNumber = extendM(
68
69
  }),
69
70
  withDefaultMake
70
71
  ),
71
- (s) => ({ withDefault: s.pipe(withDefaultConstructor(() => s(0))) })
72
+ (s) => ({ withDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) })
72
73
  )
73
74
  export type NonNegativeNumber = number & NonNegativeNumberBrand
74
75
 
@@ -13,12 +13,19 @@ export type PhoneNumber = string & PhoneNumberBrand
13
13
  export const PhoneNumber = S
14
14
  .String
15
15
  .pipe(
16
+ S.annotate({
17
+ title: "PhoneNumber",
18
+ description: "a phone number with at least 7 digits",
19
+ format: "phone"
20
+ }),
16
21
  S.refine(isValidPhone as Refinement<string, PhoneNumber>, {
17
22
  identifier: "PhoneNumber",
18
23
  title: "PhoneNumber",
19
24
  description: "a phone number with at least 7 digits",
20
- arbitrary: () => (fc: any) => Numbers(7, 10)(fc).map((_: any) => _ as PhoneNumber),
21
25
  jsonSchema: { format: "phone" }
22
26
  }),
27
+ S.annotate({
28
+ toArbitrary: () => (fc) => Numbers(7, 10)(fc).map((_) => _ as PhoneNumber)
29
+ }),
23
30
  withDefaultMake
24
31
  )
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 as any)[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
 
@@ -56,10 +58,11 @@ export const HttpClientLayer = (config: ApiConfig) =>
56
58
  HttpClientRequest.setHeaders(config.headers.pipe(Option.getOrElse(() => ({}))))
57
59
  ),
58
60
  HttpClient.mapRequestEffect((req) =>
59
- Effect.map(RequestName.asEffect(), ctx => flow(
60
- HttpClientRequest.appendUrlParam("action", ctx.requestName),
61
- HttpClientRequest.appendUrl("/" + ctx.moduleName)
62
- )(req))
61
+ Effect.map(RequestName.asEffect(), (ctx) =>
62
+ flow(
63
+ HttpClientRequest.appendUrlParam("action", ctx.requestName),
64
+ HttpClientRequest.appendUrl("/" + ctx.moduleName)
65
+ )(req))
63
66
  )
64
67
  )
65
68
  return client
@@ -75,7 +78,7 @@ export const HttpClientFromConfigLayer = Layer.unwrap(
75
78
 
76
79
  export const RpcSerializationLayer = (config: ApiConfig) =>
77
80
  Layer.mergeAll(
78
- RpcSerialization.layerJson,
81
+ RpcSerialization.layerNdjson,
79
82
  HttpClientLayer(config)
80
83
  )
81
84
 
@@ -83,7 +86,7 @@ type RpcHandlers<M extends RequestsAny> = {
83
86
  [K in keyof M]: Rpc.Rpc<M[K]["_tag"], M[K], M[K]["success"], M[K]["error"]>
84
87
  }
85
88
 
86
- const getFiltered = <M extends Requests>(resource: M) => {
89
+ const getFiltered = <M extends RequestsAny>(resource: M) => {
87
90
  type Filtered = {
88
91
  [K in keyof M as M[K] extends Req ? K : never]: M[K] extends Req ? M[K] : never
89
92
  }
@@ -101,13 +104,13 @@ const getFiltered = <M extends Requests>(resource: M) => {
101
104
  return filtered as unknown as Filtered
102
105
  }
103
106
 
104
- export const getMeta = <M extends Requests>(resource: M) => {
105
- const meta = (resource as any).meta as { moduleName: string }
106
- if (!meta) throw new Error("No meta defined in Resource!")
107
- 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!")
108
111
  }
109
112
 
110
- export const makeRpcGroupFromRequestsAndModuleName = <M extends Requests, const ModuleName extends string>(
113
+ export const makeRpcGroupFromRequestsAndModuleName = <M extends RequestsAny, const ModuleName extends string>(
111
114
  resource: M,
112
115
  moduleName: ModuleName
113
116
  ) => {
@@ -125,20 +128,13 @@ export const makeRpcGroupFromRequestsAndModuleName = <M extends Requests, const
125
128
  return rpcs
126
129
  }
127
130
 
128
- export const makeRpcGroup = <
129
- M extends Requests,
130
- const ModuleName extends string
131
- >(
132
- resource: M & { meta: { moduleName: ModuleName } }
133
- ) => makeRpcGroupFromRequestsAndModuleName(resource, resource.meta.moduleName)
134
-
135
- const makeRpcTag = <M extends Requests>(resource: M) => {
131
+ const makeRpcTag = <M extends RequestsAny>(resource: M) => {
136
132
  const meta = getMeta(resource)
137
133
  const rpcs = makeRpcGroupFromRequestsAndModuleName(resource, meta.moduleName)
138
134
 
139
135
  // Use Object.assign instead of class extension to avoid TS2509 with complex generic return types.
140
136
  // The first type arg is `any` because this is a dynamically created tag — its identity is the string key.
141
- const TheClient = ServiceMap.Opaque<
137
+ const TheClient = Context.Opaque<
142
138
  any,
143
139
  RpcClient.RpcClient<RpcGroup.Rpcs<typeof rpcs>>
144
140
  >()(`RpcClient.${meta.moduleName}`)
@@ -152,8 +148,8 @@ const makeRpcTag = <M extends Requests>(resource: M) => {
152
148
 
153
149
  const makeApiClientFactory = Effect
154
150
  .gen(function*() {
155
- const ctx = yield* Effect.services<RpcSerialization.RpcSerialization | HttpClient.HttpClient>()
156
- const makeClientFor = <M extends Requests>(
151
+ const ctx = yield* Effect.context<RpcSerialization.RpcSerialization | HttpClient.HttpClient>()
152
+ const makeClientFor = <M extends RequestsAny>(
157
153
  resource: M,
158
154
  requestLevelLayers = Layer.empty,
159
155
  options?: ClientForOptions
@@ -175,7 +171,7 @@ const makeApiClientFactory = Effect
175
171
  url: "" // why not here set meta.moduleName as root?
176
172
  })
177
173
  .pipe(
178
- Layer.provideMerge(Layer.succeedServices(ctx))
174
+ Layer.provideMerge(Layer.succeedContext(ctx))
179
175
  )
180
176
  )
181
177
  )
@@ -184,7 +180,7 @@ const makeApiClientFactory = Effect
184
180
  const filtered = getFiltered(resource)
185
181
  return {
186
182
  mr,
187
- client: (typedKeysOf(filtered)
183
+ client: typedKeysOf(filtered)
188
184
  .reduce((prev, cur) => {
189
185
  const h = filtered[cur]!
190
186
 
@@ -211,7 +207,7 @@ const makeApiClientFactory = Effect
211
207
  // @ts-expect-error doc
212
208
  prev[cur] = Object.keys(fields).length === 0
213
209
  ? {
214
- handler: mr.servicesEffect.pipe(
210
+ handler: mr.contextEffect.pipe(
215
211
  Effect.flatMap((svcs) =>
216
212
  TheClient
217
213
  .use((client) => (client as any)[requestAttr]!(new Request()) as Effect.Effect<any, any, never>)
@@ -225,7 +221,7 @@ const makeApiClientFactory = Effect
225
221
  }
226
222
  : {
227
223
  handler: (req: any) =>
228
- mr.servicesEffect.pipe(
224
+ mr.contextEffect.pipe(
229
225
  Effect.flatMap((svcs) =>
230
226
  TheClient
231
227
  .use((client) =>
@@ -242,7 +238,7 @@ const makeApiClientFactory = Effect
242
238
  }
243
239
 
244
240
  return prev
245
- }, {} as Client<M, M["meta"]["moduleName"]>))
241
+ }, {} as Client<M, ExtractModuleName<M>>)
246
242
  }
247
243
  })
248
244
 
@@ -258,9 +254,9 @@ const makeApiClientFactory = Effect
258
254
  cacheL.set(requestLevelLayers, cache)
259
255
  }
260
256
 
261
- return <M extends Requests>(
257
+ return <M extends RequestsAny>(
262
258
  models: M
263
- ): Effect.Effect<Client<M, M["meta"]["moduleName"]>> =>
259
+ ): Effect.Effect<Client<M, ExtractModuleName<M>>> =>
264
260
  Effect.gen(function*() {
265
261
  const found = cache.get(models)
266
262
  if (found) {
@@ -280,7 +276,7 @@ const makeApiClientFactory = Effect
280
276
  * Used to create clients for resource modules.
281
277
  */
282
278
  export class ApiClientFactory
283
- extends ServiceMap.Opaque<ApiClientFactory, Effect.Success<typeof makeApiClientFactory>>()("ApiClientFactory")
279
+ extends Context.Opaque<ApiClientFactory, Effect.Success<typeof makeApiClientFactory>>()("ApiClientFactory")
284
280
  {
285
281
  static readonly layer = (config: ApiConfig) =>
286
282
  ApiClientFactory.toLayer(makeApiClientFactory).pipe(Layer.provide(RpcSerializationLayer(config)))
@@ -293,7 +289,7 @@ export class ApiClientFactory
293
289
 
294
290
  static readonly makeFor =
295
291
  (requestLevelLayers: Layer.Layer<never, never, never>, options?: ClientForOptions) =>
296
- <M extends Requests>(
292
+ <M extends RequestsAny>(
297
293
  resource: M
298
294
  ) =>
299
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,