effect-app 4.0.0-beta.20 → 4.0.0-beta.201

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 (221) hide show
  1. package/CHANGELOG.md +888 -0
  2. package/dist/Array.d.ts +1 -1
  3. package/dist/Chunk.d.ts +1 -1
  4. package/dist/Chunk.d.ts.map +1 -1
  5. package/dist/Config/SecretURL.d.ts +1 -1
  6. package/dist/Config/SecretURL.d.ts.map +1 -1
  7. package/dist/Config/SecretURL.js +2 -2
  8. package/dist/Config/internal/configSecretURL.d.ts +1 -1
  9. package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
  10. package/dist/Config.d.ts +7 -0
  11. package/dist/Config.d.ts.map +1 -0
  12. package/dist/Config.js +6 -0
  13. package/dist/ConfigProvider.d.ts +39 -0
  14. package/dist/ConfigProvider.d.ts.map +1 -0
  15. package/dist/ConfigProvider.js +42 -0
  16. package/dist/Context.d.ts +40 -0
  17. package/dist/Context.d.ts.map +1 -0
  18. package/dist/Context.js +67 -0
  19. package/dist/Effect.d.ts +9 -10
  20. package/dist/Effect.d.ts.map +1 -1
  21. package/dist/Effect.js +3 -6
  22. package/dist/Function.d.ts +1 -1
  23. package/dist/Function.d.ts.map +1 -1
  24. package/dist/Inputify.type.d.ts +1 -1
  25. package/dist/Layer.d.ts +7 -6
  26. package/dist/Layer.d.ts.map +1 -1
  27. package/dist/Layer.js +1 -1
  28. package/dist/NonEmptySet.d.ts +1 -1
  29. package/dist/NonEmptySet.d.ts.map +1 -1
  30. package/dist/Option.d.ts +1 -1
  31. package/dist/Option.d.ts.map +1 -1
  32. package/dist/Pure.d.ts +5 -5
  33. package/dist/Pure.d.ts.map +1 -1
  34. package/dist/Pure.js +13 -13
  35. package/dist/Schema/Class.d.ts +66 -20
  36. package/dist/Schema/Class.d.ts.map +1 -1
  37. package/dist/Schema/Class.js +189 -22
  38. package/dist/Schema/FastCheck.d.ts +1 -1
  39. package/dist/Schema/FastCheck.d.ts.map +1 -1
  40. package/dist/Schema/Methods.d.ts +1 -1
  41. package/dist/Schema/SchemaParser.d.ts +5 -0
  42. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  43. package/dist/Schema/SchemaParser.js +6 -0
  44. package/dist/Schema/SpecialJsonSchema.d.ts +33 -0
  45. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
  46. package/dist/Schema/SpecialJsonSchema.js +122 -0
  47. package/dist/Schema/SpecialOpenApi.d.ts +32 -0
  48. package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
  49. package/dist/Schema/SpecialOpenApi.js +123 -0
  50. package/dist/Schema/brand.d.ts +7 -2
  51. package/dist/Schema/brand.d.ts.map +1 -1
  52. package/dist/Schema/brand.js +1 -1
  53. package/dist/Schema/email.d.ts +1 -1
  54. package/dist/Schema/email.d.ts.map +1 -1
  55. package/dist/Schema/email.js +7 -4
  56. package/dist/Schema/ext.d.ts +117 -45
  57. package/dist/Schema/ext.d.ts.map +1 -1
  58. package/dist/Schema/ext.js +131 -42
  59. package/dist/Schema/moreStrings.d.ts +111 -11
  60. package/dist/Schema/moreStrings.d.ts.map +1 -1
  61. package/dist/Schema/moreStrings.js +14 -15
  62. package/dist/Schema/numbers.d.ts +127 -15
  63. package/dist/Schema/numbers.d.ts.map +1 -1
  64. package/dist/Schema/numbers.js +10 -12
  65. package/dist/Schema/phoneNumber.d.ts +1 -1
  66. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  67. package/dist/Schema/phoneNumber.js +6 -3
  68. package/dist/Schema/schema.d.ts +1 -1
  69. package/dist/Schema/strings.d.ts +37 -5
  70. package/dist/Schema/strings.d.ts.map +1 -1
  71. package/dist/Schema/strings.js +1 -5
  72. package/dist/Schema.d.ts +154 -56
  73. package/dist/Schema.d.ts.map +1 -1
  74. package/dist/Schema.js +131 -64
  75. package/dist/Set.d.ts +1 -1
  76. package/dist/Set.d.ts.map +1 -1
  77. package/dist/TypeTest.d.ts +1 -1
  78. package/dist/Types.d.ts +1 -1
  79. package/dist/Widen.type.d.ts +1 -1
  80. package/dist/_ext/Array.d.ts +1 -1
  81. package/dist/_ext/Array.d.ts.map +1 -1
  82. package/dist/_ext/date.d.ts +1 -1
  83. package/dist/_ext/misc.d.ts +1 -1
  84. package/dist/_ext/ord.ext.d.ts +1 -1
  85. package/dist/_ext/ord.ext.d.ts.map +1 -1
  86. package/dist/builtin.d.ts +1 -1
  87. package/dist/builtin.d.ts.map +1 -1
  88. package/dist/client/InvalidationKeys.d.ts +29 -0
  89. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  90. package/dist/client/InvalidationKeys.js +33 -0
  91. package/dist/client/apiClientFactory.d.ts +18 -32
  92. package/dist/client/apiClientFactory.d.ts.map +1 -1
  93. package/dist/client/apiClientFactory.js +95 -32
  94. package/dist/client/clientFor.d.ts +61 -17
  95. package/dist/client/clientFor.d.ts.map +1 -1
  96. package/dist/client/clientFor.js +9 -1
  97. package/dist/client/errors.d.ts +49 -25
  98. package/dist/client/errors.d.ts.map +1 -1
  99. package/dist/client/errors.js +43 -17
  100. package/dist/client/makeClient.d.ts +468 -32
  101. package/dist/client/makeClient.d.ts.map +1 -1
  102. package/dist/client/makeClient.js +62 -23
  103. package/dist/client.d.ts +2 -1
  104. package/dist/client.d.ts.map +1 -1
  105. package/dist/client.js +2 -1
  106. package/dist/faker.d.ts +1 -1
  107. package/dist/faker.d.ts.map +1 -1
  108. package/dist/http/Request.d.ts +2 -2
  109. package/dist/http/Request.d.ts.map +1 -1
  110. package/dist/http/Request.js +5 -5
  111. package/dist/http/internal/lib.d.ts +1 -1
  112. package/dist/http.d.ts +1 -1
  113. package/dist/ids.d.ts +3 -3
  114. package/dist/ids.d.ts.map +1 -1
  115. package/dist/ids.js +3 -2
  116. package/dist/index.d.ts +5 -8
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +6 -8
  119. package/dist/logger.d.ts +1 -1
  120. package/dist/middleware.d.ts +14 -8
  121. package/dist/middleware.d.ts.map +1 -1
  122. package/dist/middleware.js +14 -8
  123. package/dist/rpc/Invalidation.d.ts +402 -0
  124. package/dist/rpc/Invalidation.d.ts.map +1 -0
  125. package/dist/rpc/Invalidation.js +150 -0
  126. package/dist/rpc/MiddlewareMaker.d.ts +5 -4
  127. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  128. package/dist/rpc/MiddlewareMaker.js +57 -37
  129. package/dist/rpc/RpcContextMap.d.ts +3 -3
  130. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  131. package/dist/rpc/RpcContextMap.js +4 -4
  132. package/dist/rpc/RpcMiddleware.d.ts +5 -4
  133. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  134. package/dist/rpc/RpcMiddleware.js +1 -1
  135. package/dist/rpc.d.ts +2 -2
  136. package/dist/rpc.d.ts.map +1 -1
  137. package/dist/rpc.js +2 -2
  138. package/dist/transform.d.ts +1 -1
  139. package/dist/transform.d.ts.map +1 -1
  140. package/dist/transform.js +3 -3
  141. package/dist/utils/effectify.d.ts +1 -1
  142. package/dist/utils/extend.d.ts +1 -1
  143. package/dist/utils/extend.d.ts.map +1 -1
  144. package/dist/utils/gen.d.ts +2 -2
  145. package/dist/utils/gen.d.ts.map +1 -1
  146. package/dist/utils/logLevel.d.ts +2 -2
  147. package/dist/utils/logLevel.d.ts.map +1 -1
  148. package/dist/utils/logger.d.ts +3 -3
  149. package/dist/utils/logger.d.ts.map +1 -1
  150. package/dist/utils/logger.js +3 -3
  151. package/dist/utils.d.ts +31 -11
  152. package/dist/utils.d.ts.map +1 -1
  153. package/dist/utils.js +13 -7
  154. package/dist/validation/validators.d.ts +1 -1
  155. package/dist/validation/validators.d.ts.map +1 -1
  156. package/dist/validation.d.ts +1 -1
  157. package/dist/validation.d.ts.map +1 -1
  158. package/package.json +46 -24
  159. package/src/Config/SecretURL.ts +2 -1
  160. package/src/Config.ts +14 -0
  161. package/src/ConfigProvider.ts +48 -0
  162. package/src/{ServiceMap.ts → Context.ts} +52 -59
  163. package/src/Effect.ts +12 -14
  164. package/src/Layer.ts +6 -5
  165. package/src/Pure.ts +17 -18
  166. package/src/Schema/Class.ts +268 -62
  167. package/src/Schema/SchemaParser.ts +12 -0
  168. package/src/Schema/SpecialJsonSchema.ts +137 -0
  169. package/src/Schema/SpecialOpenApi.ts +130 -0
  170. package/src/Schema/brand.ts +9 -1
  171. package/src/Schema/email.ts +7 -2
  172. package/src/Schema/ext.ts +204 -72
  173. package/src/Schema/moreStrings.ts +22 -20
  174. package/src/Schema/numbers.ts +14 -16
  175. package/src/Schema/phoneNumber.ts +5 -1
  176. package/src/Schema/strings.ts +4 -8
  177. package/src/Schema.ts +332 -105
  178. package/src/client/InvalidationKeys.ts +50 -0
  179. package/src/client/apiClientFactory.ts +220 -129
  180. package/src/client/clientFor.ts +97 -29
  181. package/src/client/errors.ts +52 -26
  182. package/src/client/makeClient.ts +538 -68
  183. package/src/client.ts +1 -0
  184. package/src/http/Request.ts +7 -4
  185. package/src/ids.ts +2 -1
  186. package/src/index.ts +5 -10
  187. package/src/middleware.ts +13 -9
  188. package/src/rpc/Invalidation.ts +226 -0
  189. package/src/rpc/MiddlewareMaker.ts +65 -60
  190. package/src/rpc/README.md +2 -2
  191. package/src/rpc/RpcContextMap.ts +6 -5
  192. package/src/rpc/RpcMiddleware.ts +5 -4
  193. package/src/rpc.ts +1 -1
  194. package/src/transform.ts +2 -2
  195. package/src/utils/gen.ts +1 -1
  196. package/src/utils/logger.ts +2 -2
  197. package/src/utils.ts +51 -15
  198. package/test/dist/rpc.test.d.ts.map +1 -1
  199. package/test/dist/secretURL.test.d.ts.map +1 -0
  200. package/test/dist/special.test.d.ts.map +1 -0
  201. package/test/dist/stream-error.types.d.ts +2 -0
  202. package/test/dist/stream-error.types.d.ts.map +1 -0
  203. package/test/dist/stream-error.types.js +27 -0
  204. package/test/rpc.test.ts +41 -6
  205. package/test/schema.test.ts +591 -17
  206. package/test/secretURL.test.ts +157 -0
  207. package/test/special.test.ts +1023 -0
  208. package/test/utils.test.ts +6 -6
  209. package/tsconfig.base.json +3 -4
  210. package/tsconfig.json +0 -1
  211. package/tsconfig.json.bak +2 -2
  212. package/tsconfig.src.json +29 -29
  213. package/tsconfig.test.json +2 -2
  214. package/dist/Operations.d.ts +0 -87
  215. package/dist/Operations.d.ts.map +0 -1
  216. package/dist/Operations.js +0 -29
  217. package/dist/ServiceMap.d.ts +0 -44
  218. package/dist/ServiceMap.d.ts.map +0 -1
  219. package/dist/ServiceMap.js +0 -91
  220. package/eslint.config.mjs +0 -26
  221. package/src/Operations.ts +0 -55
@@ -1,4 +1,6 @@
1
- import { type GetContextConfig, type GetEffectError, type RequestContextMapTagAny } from "../rpc/RpcContextMap.js"
1
+ import { SchemaTransformation } from "effect"
2
+ import type * as Exit from "effect/Exit"
3
+ import { type GetContextConfig, type RequestContextMapTagAny } from "../rpc/RpcContextMap.js"
2
4
  import * as S from "../Schema.js"
3
5
  import { AST } from "../Schema.js"
4
6
 
@@ -8,103 +10,571 @@ const merge = (a: any, b: Array<any>) =>
8
10
  /**
9
11
  * Whatever the input, we will only decode or encode to void
10
12
  */
11
- const ForceVoid: S.Codec<void> = S.Void as any
13
+ export const ForceVoid = S
14
+ .declare((_: unknown): _ is unknown => true)
15
+ .pipe(
16
+ S.decodeTo(S.Any, SchemaTransformation.transform<unknown, unknown>({ decode: () => void 0, encode: () => void 0 }))
17
+ )
12
18
 
13
19
  type SchemaOrFields<T> = T extends S.Top ? T : T extends S.Struct.Fields ? S.Struct<T> : S.Void
14
20
 
15
- type TaggedRequestResult<
21
+ type TaggedRequestSchema<Tag extends string, Payload extends S.Struct.Fields> = S.Struct<
22
+ { readonly _tag: S.tag<Tag> } & Payload
23
+ >
24
+
25
+ type QueryOnlyRequests<Resource> = {
26
+ [K in keyof Resource as Resource[K] extends { readonly type: "query" } ? K : never]: Resource[K]
27
+ }
28
+
29
+ type QueryOnlyResources<Resources> = {
30
+ [K in keyof Resources]: QueryOnlyRequests<Resources[K]>
31
+ }
32
+
33
+ type InputFromPayload<Payload extends S.Struct.Fields> = keyof Payload extends never ? void
34
+ : S.Schema.Type<S.Struct<Payload>>
35
+
36
+ type OutputFromSuccess<Success extends S.Top> = Success extends typeof ForceVoid ? void : S.Schema.Type<Success>
37
+
38
+ type InvalidationResources = Record<string, Record<string, unknown>>
39
+
40
+ export type InvalidateQueryInstruction = {
41
+ readonly filters?: Record<string, unknown>
42
+ readonly options?: Record<string, unknown>
43
+ }
44
+
45
+ export type InvalidationCallback<Resources, Input = unknown, Success = unknown, Failure = unknown> = (
46
+ queryKey: readonly string[],
47
+ resources: QueryOnlyResources<Resources>,
48
+ ...args: [Input] extends [void] ? [exit: Exit.Exit<Success, Failure>]
49
+ : [input: Input, exit: Exit.Exit<Success, Failure>]
50
+ ) => ReadonlyArray<InvalidateQueryInstruction>
51
+
52
+ export type InvalidationConfig<Resources, Input = unknown, Success = unknown, Failure = unknown> = {
53
+ readonly invalidatesQueries: InvalidationCallback<Resources, Input, Success, Failure>
54
+ readonly invalidationResources?: Resources
55
+ }
56
+
57
+ type InvalidationConfigForCommand<
58
+ Resources,
59
+ Payload extends S.Struct.Fields,
60
+ Success extends S.Top,
61
+ Error extends S.Top
62
+ > = InvalidationConfig<
63
+ Resources,
64
+ InputFromPayload<Payload>,
65
+ OutputFromSuccess<Success>,
66
+ S.Schema.Type<Error>
67
+ >
68
+
69
+ export const configureInvalidation = <Resources>() =>
70
+ <Input, Success, Failure>(
71
+ invalidatesQueries: InvalidationCallback<Resources, NoInfer<Input>, NoInfer<Success>, NoInfer<Failure>>
72
+ ): InvalidationConfig<Resources, Input, Success, Failure> => ({ invalidatesQueries })
73
+
74
+ export const configureInvalidationCallback = <Resources>() =>
75
+ <Input, Success, Failure>(
76
+ invalidatesQueries: InvalidationCallback<Resources, NoInfer<Input>, NoInfer<Success>, NoInfer<Failure>>
77
+ ): InvalidationCallback<Resources, Input, Success, Failure> => invalidatesQueries
78
+
79
+ export const configureInvalidationResources = <Resources>() =>
80
+ ({}) as Pick<InvalidationConfig<Resources>, "invalidationResources">
81
+
82
+ type TaggedRequestForResult<
83
+ Self,
16
84
  Tag extends string,
17
85
  Payload extends S.Struct.Fields,
18
86
  Success extends S.Top,
19
87
  Error extends S.Top,
20
- Config = Record<string, never>
88
+ Config,
89
+ ModuleName extends string,
90
+ Type extends "command" | "query",
91
+ Stream extends boolean,
92
+ Resources = never,
93
+ Final extends S.Top = never
21
94
  > =
22
- & S.TaggedStruct<Tag, Payload>
95
+ & S.Opaque<Self, S.ExtendedSchemaNoEncoded, TaggedRequestSchema<Tag, Payload>, {}>
23
96
  & {
24
- new(...args: any[]): any
97
+ readonly fields: TaggedRequestSchema<Tag, Payload>["fields"]
25
98
  readonly _tag: Tag
26
- readonly fields: { readonly _tag: S.tag<Tag> } & Payload
27
99
  readonly success: Success
28
100
  readonly error: Error
29
101
  readonly config: Config
30
- readonly "~decodingServices": S.Codec.DecodingServices<Success> | S.Codec.DecodingServices<Error>
102
+ readonly id: `${ModuleName}.${Tag}`
103
+ readonly moduleName: ModuleName
104
+ readonly type: Type
105
+ readonly stream: Stream
106
+ readonly "~invalidationResources"?: Resources
31
107
  }
108
+ & ([Final] extends [never] ? {} : { readonly final: Final })
32
109
 
33
110
  export const makeRpcClient = <
34
111
  RequestContextMap extends RequestContextMapTagAny,
35
112
  GeneralErrors extends S.Top = never
36
113
  >(rcs: RequestContextMap, generalErrors?: GeneralErrors) => {
37
- // Long way around ServiceMap/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??)
114
+ // Long way around Context/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??)
38
115
  type ServiceMap = {
39
116
  success: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
40
117
  error: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
118
+ final?: S.Top | S.Struct.Fields // optional final-value schema for stream requests
119
+ stream?: boolean // request metadata — stripped from stored config
41
120
  }
42
121
 
43
122
  type RequestConfig = GetContextConfig<RequestContextMap["config"]>
44
123
 
124
+ // Errors raised by RPC middleware (e.g. `NotLoggedInError` from auth) used to
125
+ // be merged into `resource.error` here so they would surface in the wire
126
+ // failure schema. That merging is gone — the canonical source for middleware
127
+ // errors is now the middleware tag attached to the rpc on both server and
128
+ // client (see `ApiClientFactory.makeFor({ middleware })`). `Rpc.exitSchema`
129
+ // unions in `rpc.middlewares[*].error` automatically.
45
130
  type MergeError<E> = [GeneralErrors] extends [never] ? SchemaOrFields<E> : S.Union<[SchemaOrFields<E>, GeneralErrors]>
46
131
  type ErrorResult<C> = C extends { error: infer E } ? MergeError<E>
47
- : [GeneralErrors] extends [never] ? GetEffectError<RequestContextMap["config"], C>
48
- : MergeError<GetEffectError<RequestContextMap["config"], C>>
49
-
50
- function TaggedRequest<_Self>(): {
51
- <Tag extends string, Payload extends S.Struct.Fields, C extends ServiceMap>(
52
- tag: Tag,
53
- fields: Payload,
54
- config: RequestConfig & C
55
- ): TaggedRequestResult<Tag, Payload, SchemaOrFields<C["success"]>, ErrorResult<C>, Omit<C, "success" | "error">>
56
- <Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "success">>(
57
- tag: Tag,
58
- fields: Payload,
59
- config: RequestConfig & C
60
- ): TaggedRequestResult<Tag, Payload, SchemaOrFields<C["success"]>, ErrorResult<C>, Omit<C, "success" | "error">>
61
- <Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "error">>(
62
- tag: Tag,
63
- fields: Payload,
64
- config: RequestConfig & C
65
- ): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<C>, Omit<C, "success" | "error">>
66
- <Tag extends string, Payload extends S.Struct.Fields, C extends Record<string, any>>(
67
- tag: Tag,
68
- fields: Payload,
69
- config: C & RequestConfig
70
- ): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<C>, Omit<C, "success" | "error">>
71
- <Tag extends string, Payload extends S.Struct.Fields>(
72
- tag: Tag,
73
- fields: Payload
74
- ): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<{}>, Record<string, never>>
75
- } {
76
- // TODO: filter errors based on config + take care of inversion
77
- const errorSchemas = Object.values(rcs.config).map((_) => _.error)
78
- return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
79
- tag: Tag,
80
- fields: Fields,
81
- config?: C
82
- ) => {
83
- // TODO: S.TaggedRequest removed in v4 — needs rework to use Rpc.make or Request.TaggedClass
84
- // For now, creating a simple tagged struct class with success/failure properties
85
- const failureSchema = merge(
86
- config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined,
87
- [...errorSchemas, generalErrors].filter(Boolean)
88
- )
89
- const successSchema = config?.success
90
- ? S.isSchema(config.success)
91
- ? AST.isVoid(config.success.ast) ? ForceVoid : config.success
92
- : S.Struct(config.success)
93
- : ForceVoid
94
-
95
- const RequestClass = S.TaggedClass<any>()(tag, fields)
96
- Object.assign(RequestClass, {
97
- _tag: tag,
98
- success: successSchema,
99
- error: failureSchema,
100
- config
101
- })
102
-
103
- return RequestClass
104
- }) as any
132
+ : [GeneralErrors] extends [never] ? typeof S.Never
133
+ : GeneralErrors
134
+
135
+ // Suppress unused-warning for the rcm parameter — it's still load-bearing for
136
+ // the type-side `RequestConfig` definition above.
137
+ void rcs
138
+
139
+ function makeRequestClass<Tag extends string, Fields extends S.Struct.Fields, C extends Partial<ServiceMap>>(
140
+ tag: Tag,
141
+ fields: Fields,
142
+ config?: C
143
+ ) {
144
+ const failureSchema = merge(
145
+ config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined,
146
+ [generalErrors].filter(Boolean)
147
+ )
148
+ const successSchema = config?.success
149
+ ? S.isSchema(config.success)
150
+ ? AST.isVoid(config.success.ast) ? ForceVoid : config.success
151
+ : S.Struct(config.success)
152
+ : ForceVoid
153
+
154
+ const finalConfig = (config as any)?.final
155
+ const finalSchema = finalConfig && S.isSchema(finalConfig) ? finalConfig : undefined
156
+
157
+ // Strip stream from the stored config — it's request metadata, not handler config
158
+ const { stream: _stream, ...restConfig } = config ?? ({} as C)
159
+
160
+ const RequestClass = S.Opaque()(S.TaggedStruct(tag, fields))
161
+ Object.assign(RequestClass, {
162
+ _tag: tag,
163
+ success: successSchema,
164
+ error: failureSchema,
165
+ ...(finalSchema !== undefined ? { final: finalSchema } : {}),
166
+ config: restConfig
167
+ })
168
+
169
+ return RequestClass
170
+ }
171
+
172
+ function makeTaggedRequestWithMeta<
173
+ ModuleName extends string,
174
+ Type extends "command" | "query"
175
+ >(
176
+ moduleName: ModuleName,
177
+ type: Type
178
+ ) {
179
+ function TaggedRequestWithMeta<Self, Resources extends InvalidationResources = never>(): {
180
+ // ─── stream: true overloads (must come before non-stream so TypeScript picks them) ───────────
181
+ <
182
+ Tag extends string,
183
+ Payload extends S.Struct.Fields,
184
+ Success extends S.Top | S.Struct.Fields,
185
+ Error extends S.Top | S.Struct.Fields,
186
+ Final extends S.Top | S.Struct.Fields = never,
187
+ C extends RequestConfig & Record<string, any> = RequestConfig & Record<string, any>
188
+ >(
189
+ tag: Tag,
190
+ fields: Payload,
191
+ config:
192
+ & Omit<C, "invalidatesQueries">
193
+ & { stream: true; success: Success; error: Error; final?: Final },
194
+ invalidatesQueries?: InvalidationCallback<
195
+ Resources,
196
+ InputFromPayload<Payload>,
197
+ OutputFromSuccess<SchemaOrFields<Success>>,
198
+ S.Schema.Type<ErrorResult<C & { success: Success; error: Error }>>
199
+ >
200
+ ): TaggedRequestForResult<
201
+ Self,
202
+ Tag,
203
+ Payload,
204
+ SchemaOrFields<Success>,
205
+ ErrorResult<C & { success: Success; error: Error }>,
206
+ Omit<
207
+ & Omit<C, "invalidatesQueries">
208
+ & {
209
+ success: Success
210
+ error: Error
211
+ }
212
+ & Partial<
213
+ InvalidationConfigForCommand<
214
+ Resources,
215
+ Payload,
216
+ SchemaOrFields<Success>,
217
+ ErrorResult<C & { success: Success; error: Error }>
218
+ >
219
+ >,
220
+ "success" | "error" | "stream"
221
+ >,
222
+ ModuleName,
223
+ Type,
224
+ true,
225
+ Resources,
226
+ [Final] extends [never] ? never : SchemaOrFields<Final>
227
+ >
228
+ <
229
+ Tag extends string,
230
+ Payload extends S.Struct.Fields,
231
+ Success extends S.Top | S.Struct.Fields,
232
+ Final extends S.Top | S.Struct.Fields = never,
233
+ C extends RequestConfig & Record<string, any> & { error?: never } =
234
+ & RequestConfig
235
+ & Record<string, any>
236
+ & { error?: never }
237
+ >(
238
+ tag: Tag,
239
+ fields: Payload,
240
+ config:
241
+ & Omit<C, "invalidatesQueries">
242
+ & { stream: true; success: Success; final?: Final },
243
+ invalidatesQueries?: InvalidationCallback<
244
+ Resources,
245
+ InputFromPayload<Payload>,
246
+ OutputFromSuccess<SchemaOrFields<Success>>,
247
+ S.Schema.Type<ErrorResult<C & { success: Success }>>
248
+ >
249
+ ): TaggedRequestForResult<
250
+ Self,
251
+ Tag,
252
+ Payload,
253
+ SchemaOrFields<Success>,
254
+ ErrorResult<C & { success: Success }>,
255
+ Omit<
256
+ & Omit<C, "invalidatesQueries">
257
+ & { success: Success }
258
+ & Partial<
259
+ InvalidationConfigForCommand<
260
+ Resources,
261
+ Payload,
262
+ SchemaOrFields<Success>,
263
+ ErrorResult<C & { success: Success }>
264
+ >
265
+ >,
266
+ "success" | "error" | "stream"
267
+ >,
268
+ ModuleName,
269
+ Type,
270
+ true,
271
+ Resources,
272
+ [Final] extends [never] ? never : SchemaOrFields<Final>
273
+ >
274
+ // ─── stream: true without `success` overloads ────────────────────────────────────────────────
275
+ <
276
+ Tag extends string,
277
+ Payload extends S.Struct.Fields,
278
+ Error extends S.Top | S.Struct.Fields,
279
+ C extends RequestConfig & Record<string, any> & { success?: never } =
280
+ & RequestConfig
281
+ & Record<string, any>
282
+ & { success?: never }
283
+ >(
284
+ tag: Tag,
285
+ fields: Payload,
286
+ config:
287
+ & Omit<C, "invalidatesQueries">
288
+ & { stream: true; error: Error },
289
+ invalidatesQueries?: InvalidationCallback<
290
+ Resources,
291
+ InputFromPayload<Payload>,
292
+ void,
293
+ S.Schema.Type<ErrorResult<C & { error: Error }>>
294
+ >
295
+ ): TaggedRequestForResult<
296
+ Self,
297
+ Tag,
298
+ Payload,
299
+ typeof ForceVoid,
300
+ ErrorResult<C & { error: Error }>,
301
+ Omit<
302
+ & Omit<C, "invalidatesQueries">
303
+ & { error: Error }
304
+ & Partial<
305
+ InvalidationConfigForCommand<
306
+ Resources,
307
+ Payload,
308
+ typeof ForceVoid,
309
+ ErrorResult<C & { error: Error }>
310
+ >
311
+ >,
312
+ "success" | "error" | "stream"
313
+ >,
314
+ ModuleName,
315
+ Type,
316
+ true,
317
+ Resources
318
+ >
319
+ <
320
+ Tag extends string,
321
+ Payload extends S.Struct.Fields,
322
+ C extends RequestConfig & Record<string, any> & { success?: never; error?: never } =
323
+ & RequestConfig
324
+ & Record<string, any>
325
+ & { success?: never; error?: never }
326
+ >(
327
+ tag: Tag,
328
+ fields: Payload,
329
+ config:
330
+ & Omit<C, "invalidatesQueries">
331
+ & { stream: true },
332
+ invalidatesQueries?: InvalidationCallback<
333
+ Resources,
334
+ InputFromPayload<Payload>,
335
+ void,
336
+ S.Schema.Type<ErrorResult<C>>
337
+ >
338
+ ): TaggedRequestForResult<
339
+ Self,
340
+ Tag,
341
+ Payload,
342
+ typeof ForceVoid,
343
+ ErrorResult<C>,
344
+ Omit<
345
+ & Omit<C, "invalidatesQueries">
346
+ & Partial<InvalidationConfigForCommand<Resources, Payload, typeof ForceVoid, ErrorResult<C>>>,
347
+ "success" | "error" | "stream"
348
+ >,
349
+ ModuleName,
350
+ Type,
351
+ true,
352
+ Resources
353
+ >
354
+ // ─── non-stream overloads ────────────────────────────────────────────────────────────────────
355
+ <
356
+ Tag extends string,
357
+ Payload extends S.Struct.Fields,
358
+ Success extends S.Top | S.Struct.Fields,
359
+ Error extends S.Top | S.Struct.Fields,
360
+ Final extends S.Top | S.Struct.Fields = never,
361
+ C extends RequestConfig & Record<string, any> = RequestConfig & Record<string, any>
362
+ >(
363
+ tag: Tag,
364
+ fields: Payload,
365
+ config:
366
+ & Omit<C, "invalidatesQueries">
367
+ & { success: Success; error: Error; final?: Final },
368
+ invalidatesQueries?: InvalidationCallback<
369
+ Resources,
370
+ InputFromPayload<Payload>,
371
+ OutputFromSuccess<SchemaOrFields<Success>>,
372
+ S.Schema.Type<ErrorResult<C & { success: Success; error: Error }>>
373
+ >
374
+ ): TaggedRequestForResult<
375
+ Self,
376
+ Tag,
377
+ Payload,
378
+ SchemaOrFields<Success>,
379
+ ErrorResult<C & { success: Success; error: Error }>,
380
+ Omit<
381
+ & Omit<C, "invalidatesQueries">
382
+ & {
383
+ success: Success
384
+ error: Error
385
+ }
386
+ & Partial<
387
+ InvalidationConfigForCommand<
388
+ Resources,
389
+ Payload,
390
+ SchemaOrFields<Success>,
391
+ ErrorResult<C & { success: Success; error: Error }>
392
+ >
393
+ >,
394
+ "success" | "error" | "stream"
395
+ >,
396
+ ModuleName,
397
+ Type,
398
+ false,
399
+ Resources,
400
+ [Final] extends [never] ? never : SchemaOrFields<Final>
401
+ >
402
+ <
403
+ Tag extends string,
404
+ Payload extends S.Struct.Fields,
405
+ Success extends S.Top | S.Struct.Fields,
406
+ Final extends S.Top | S.Struct.Fields = never,
407
+ C extends RequestConfig & Record<string, any> & { error?: never } =
408
+ & RequestConfig
409
+ & Record<string, any>
410
+ & {
411
+ error?: never
412
+ }
413
+ >(
414
+ tag: Tag,
415
+ fields: Payload,
416
+ config:
417
+ & Omit<C, "invalidatesQueries">
418
+ & { success: Success; final?: Final },
419
+ invalidatesQueries?: InvalidationCallback<
420
+ Resources,
421
+ InputFromPayload<Payload>,
422
+ OutputFromSuccess<SchemaOrFields<Success>>,
423
+ S.Schema.Type<ErrorResult<C & { success: Success }>>
424
+ >
425
+ ): TaggedRequestForResult<
426
+ Self,
427
+ Tag,
428
+ Payload,
429
+ SchemaOrFields<Success>,
430
+ ErrorResult<C & { success: Success }>,
431
+ Omit<
432
+ & Omit<C, "invalidatesQueries">
433
+ & {
434
+ success: Success
435
+ }
436
+ & Partial<
437
+ InvalidationConfigForCommand<
438
+ Resources,
439
+ Payload,
440
+ SchemaOrFields<Success>,
441
+ ErrorResult<C & { success: Success }>
442
+ >
443
+ >,
444
+ "success" | "error" | "stream"
445
+ >,
446
+ ModuleName,
447
+ Type,
448
+ false,
449
+ Resources,
450
+ [Final] extends [never] ? never : SchemaOrFields<Final>
451
+ >
452
+ <
453
+ Tag extends string,
454
+ Payload extends S.Struct.Fields,
455
+ Error extends S.Top | S.Struct.Fields,
456
+ C extends RequestConfig & Record<string, any> & { success?: never }
457
+ >(
458
+ tag: Tag,
459
+ fields: Payload,
460
+ config:
461
+ & Omit<C, "invalidatesQueries">
462
+ & { error: Error },
463
+ invalidatesQueries?: InvalidationCallback<
464
+ Resources,
465
+ InputFromPayload<Payload>,
466
+ void,
467
+ S.Schema.Type<ErrorResult<C & { error: Error }>>
468
+ >
469
+ ): TaggedRequestForResult<
470
+ Self,
471
+ Tag,
472
+ Payload,
473
+ typeof ForceVoid,
474
+ ErrorResult<C & { error: Error }>,
475
+ Omit<
476
+ & Omit<C, "invalidatesQueries">
477
+ & {
478
+ error: Error
479
+ }
480
+ & Partial<
481
+ InvalidationConfigForCommand<
482
+ Resources,
483
+ Payload,
484
+ typeof ForceVoid,
485
+ ErrorResult<C & { error: Error }>
486
+ >
487
+ >,
488
+ "success" | "error" | "stream"
489
+ >,
490
+ ModuleName,
491
+ Type,
492
+ false,
493
+ Resources
494
+ >
495
+ <
496
+ Tag extends string,
497
+ Payload extends S.Struct.Fields,
498
+ C extends RequestConfig & Record<string, any> & { success?: never; error?: never }
499
+ >(
500
+ tag: Tag,
501
+ fields: Payload,
502
+ config: Omit<C, "invalidatesQueries">,
503
+ invalidatesQueries?: InvalidationCallback<
504
+ Resources,
505
+ InputFromPayload<Payload>,
506
+ void,
507
+ S.Schema.Type<ErrorResult<C>>
508
+ >
509
+ ): TaggedRequestForResult<
510
+ Self,
511
+ Tag,
512
+ Payload,
513
+ typeof ForceVoid,
514
+ ErrorResult<C>,
515
+ Omit<
516
+ & Omit<C, "invalidatesQueries">
517
+ & Partial<InvalidationConfigForCommand<Resources, Payload, typeof ForceVoid, ErrorResult<C>>>,
518
+ "success" | "error" | "stream"
519
+ >,
520
+ ModuleName,
521
+ Type,
522
+ false,
523
+ Resources
524
+ >
525
+ <Tag extends string, Payload extends S.Struct.Fields>(
526
+ tag: Tag,
527
+ fields: Payload
528
+ ): TaggedRequestForResult<
529
+ Self,
530
+ Tag,
531
+ Payload,
532
+ typeof ForceVoid,
533
+ ErrorResult<{}>,
534
+ Record<string, never>,
535
+ ModuleName,
536
+ Type,
537
+ false
538
+ >
539
+ } {
540
+ return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
541
+ tag: Tag,
542
+ fields: Fields,
543
+ config?: C,
544
+ invalidatesQueries?: InvalidationCallback<Resources>
545
+ ) => {
546
+ const isStream = (config as any)?.stream === true
547
+ const requestConfig = invalidatesQueries === undefined ? config : { ...config, invalidatesQueries }
548
+ const cls = makeRequestClass(tag, fields, requestConfig)
549
+ Object.assign(cls, { id: `${moduleName}.${tag}`, moduleName, type, stream: isStream })
550
+ return cls
551
+ }) as any
552
+ }
553
+ return Object.assign(TaggedRequestWithMeta, { moduleName, type } as const)
554
+ }
555
+
556
+ function TaggedRequestFor<ModuleName extends string>(moduleName: ModuleName) {
557
+ const Query = makeTaggedRequestWithMeta(moduleName, "query")
558
+ const Command = makeTaggedRequestWithMeta(moduleName, "command")
559
+
560
+ return {
561
+ moduleName,
562
+ /**
563
+ * Create query request classes for this module.
564
+ * Queries read state and should not mutate server state.
565
+ * Pass `stream: true` in the config to produce a Stream of `success` values (QueryStream behaviour).
566
+ */
567
+ Query,
568
+ /**
569
+ * Create command request classes for this module.
570
+ * Commands mutate state and should avoid returning complex read models.
571
+ * Pass `stream: true` in the config to produce a Stream of `success` values (CommandStream behaviour).
572
+ */
573
+ Command
574
+ } as const
105
575
  }
106
576
 
107
577
  return {
108
- TaggedRequest
578
+ TaggedRequestFor
109
579
  }
110
580
  }
package/src/client.ts CHANGED
@@ -2,5 +2,6 @@
2
2
  export * from "./client/apiClientFactory.js"
3
3
  export * from "./client/clientFor.js"
4
4
  export * from "./client/errors.js"
5
+ export * from "./client/InvalidationKeys.js"
5
6
  export * from "./client/makeClient.js"
6
7
  // codegen:end
@@ -1,3 +1,4 @@
1
+ import { Option } from "effect"
1
2
  import type { HttpClientResponse } from "effect/unstable/http/HttpClientResponse"
2
3
  import * as Effect from "../Effect.js"
3
4
  import { HttpClient, HttpClientError, HttpClientRequest, HttpHeaders } from "./internal/lib.js"
@@ -24,16 +25,18 @@ export const demandJson = (client: HttpClient.HttpClient) =>
24
25
  .mapRequest(client, (_) => HttpClientRequest.acceptJson(_))
25
26
  .pipe(HttpClient.transform((r, request) =>
26
27
  Effect.tap(r, (response) =>
27
- HttpHeaders
28
- .get(response.headers, "Content-Type")
29
- ?.startsWith("application/json")
28
+ Option
29
+ .exists(
30
+ HttpHeaders.get(response.headers, "Content-Type"),
31
+ (_) => _.startsWith("application/json")
32
+ )
30
33
  ? Effect.void
31
34
  : Effect.fail(
32
35
  new HttpClientError.DecodeError({
33
36
  request,
34
37
  response,
35
38
  description: "not json response: "
36
- + HttpHeaders.get(response.headers, "Content-Type")
39
+ + Option.getOrElse(HttpHeaders.get(response.headers, "Content-Type"), () => "<missing>")
37
40
  })
38
41
  ))
39
42
  ))