effect-app 4.0.0-beta.2 → 4.0.0-beta.200

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 (224) hide show
  1. package/CHANGELOG.md +960 -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 +14 -6
  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 +9 -5
  56. package/dist/Schema/ext.d.ts +121 -48
  57. package/dist/Schema/ext.d.ts.map +1 -1
  58. package/dist/Schema/ext.js +134 -52
  59. package/dist/Schema/moreStrings.d.ts +117 -17
  60. package/dist/Schema/moreStrings.d.ts.map +1 -1
  61. package/dist/Schema/moreStrings.js +19 -18
  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 +8 -4
  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 +159 -58
  73. package/dist/Schema.d.ts.map +1 -1
  74. package/dist/Schema.js +136 -68
  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 +98 -36
  94. package/dist/client/clientFor.d.ts +51 -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 +61 -34
  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 +9 -9
  114. package/dist/ids.d.ts.map +1 -1
  115. package/dist/ids.js +3 -2
  116. package/dist/index.d.ts +5 -9
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +6 -9
  119. package/dist/logger.d.ts +1 -1
  120. package/dist/middleware.d.ts +16 -9
  121. package/dist/middleware.d.ts.map +1 -1
  122. package/dist/middleware.js +13 -9
  123. package/dist/rpc/Invalidation.d.ts +397 -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 +6 -5
  127. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  128. package/dist/rpc/MiddlewareMaker.js +51 -28
  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 +6 -5
  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 +48 -10
  152. package/dist/utils.d.ts.map +1 -1
  153. package/dist/utils.js +33 -8
  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 -28
  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} +58 -64
  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 +21 -7
  171. package/src/Schema/email.ts +10 -3
  172. package/src/Schema/ext.ts +226 -86
  173. package/src/Schema/moreStrings.ts +37 -32
  174. package/src/Schema/numbers.ts +14 -16
  175. package/src/Schema/phoneNumber.ts +8 -2
  176. package/src/Schema/strings.ts +4 -8
  177. package/src/Schema.ts +350 -107
  178. package/src/client/InvalidationKeys.ts +50 -0
  179. package/src/client/apiClientFactory.ts +227 -136
  180. package/src/client/clientFor.ts +86 -29
  181. package/src/client/errors.ts +61 -26
  182. package/src/client/makeClient.ts +530 -80
  183. package/src/client.ts +1 -0
  184. package/src/http/Request.ts +7 -4
  185. package/src/ids.ts +3 -2
  186. package/src/index.ts +5 -11
  187. package/src/middleware.ts +12 -10
  188. package/src/rpc/Invalidation.ts +221 -0
  189. package/src/rpc/MiddlewareMaker.ts +61 -51
  190. package/src/rpc/README.md +2 -2
  191. package/src/rpc/RpcContextMap.ts +6 -5
  192. package/src/rpc/RpcMiddleware.ts +6 -5
  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 +73 -15
  198. package/test/dist/moreStrings.test.d.ts.map +1 -0
  199. package/test/dist/rpc.test.d.ts.map +1 -1
  200. package/test/dist/secretURL.test.d.ts.map +1 -0
  201. package/test/dist/special.test.d.ts.map +1 -0
  202. package/test/moreStrings.test.ts +17 -0
  203. package/test/rpc.test.ts +38 -6
  204. package/test/schema.test.ts +609 -4
  205. package/test/secretURL.test.ts +157 -0
  206. package/test/special.test.ts +1023 -0
  207. package/test/utils.test.ts +6 -6
  208. package/tsconfig.base.json +3 -4
  209. package/tsconfig.json +0 -1
  210. package/tsconfig.json.bak +2 -2
  211. package/tsconfig.src.json +29 -29
  212. package/tsconfig.test.json +2 -2
  213. package/dist/Operations.d.ts +0 -87
  214. package/dist/Operations.d.ts.map +0 -1
  215. package/dist/Operations.js +0 -29
  216. package/dist/ServiceMap.d.ts +0 -44
  217. package/dist/ServiceMap.d.ts.map +0 -1
  218. package/dist/ServiceMap.js +0 -91
  219. package/dist/Struct.d.ts +0 -44
  220. package/dist/Struct.d.ts.map +0 -1
  221. package/dist/Struct.js +0 -29
  222. package/eslint.config.mjs +0 -26
  223. package/src/Operations.ts +0 -55
  224. package/src/Struct.ts +0 -54
@@ -1,4 +1,6 @@
1
- import { type GetContextConfig, type RequestContextMapTagAny } from "../rpc/RpcContextMap.js"
1
+ import { SchemaTransformation } from "effect"
2
+ import type * as Exit from "effect/Exit"
3
+ import { type GetContextConfig, type GetEffectError, type RequestContextMapTagAny } from "../rpc/RpcContextMap.js"
2
4
  import * as S from "../Schema.js"
3
5
  import { AST } from "../Schema.js"
4
6
 
@@ -8,116 +10,564 @@ 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
 
45
124
  type MergeError<E> = [GeneralErrors] extends [never] ? SchemaOrFields<E> : S.Union<[SchemaOrFields<E>, GeneralErrors]>
46
125
  type ErrorResult<C> = C extends { error: infer E } ? MergeError<E>
47
- : [GeneralErrors] extends [never] ? S.Void
48
- : GeneralErrors
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<never>, 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 payloadSchema = S.Struct({ _tag: S.tag(tag), ...fields })
96
-
97
- const taggedFields = { _tag: S.tag(tag), ...fields }
98
-
99
- const RequestClass = class {
100
- constructor(payload?: any) {
101
- if (payload) {
102
- Object.assign(this, payload)
126
+ : [GeneralErrors] extends [never] ? GetEffectError<RequestContextMap["config"], C>
127
+ : MergeError<GetEffectError<RequestContextMap["config"], C>>
128
+
129
+ // TODO: filter errors based on config + take care of inversion
130
+ const errorSchemas = Object.values(rcs.config).map((_) => _.error)
131
+
132
+ function makeRequestClass<Tag extends string, Fields extends S.Struct.Fields, C extends Partial<ServiceMap>>(
133
+ tag: Tag,
134
+ fields: Fields,
135
+ config?: C
136
+ ) {
137
+ const failureSchema = merge(
138
+ config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined,
139
+ [...errorSchemas, generalErrors].filter(Boolean)
140
+ )
141
+ const successSchema = config?.success
142
+ ? S.isSchema(config.success)
143
+ ? AST.isVoid(config.success.ast) ? ForceVoid : config.success
144
+ : S.Struct(config.success)
145
+ : ForceVoid
146
+
147
+ const finalConfig = (config as any)?.final
148
+ const finalSchema = finalConfig && S.isSchema(finalConfig) ? finalConfig : undefined
149
+
150
+ // Strip stream from the stored config — it's request metadata, not handler config
151
+ const { stream: _stream, ...restConfig } = config ?? ({} as C)
152
+
153
+ const RequestClass = S.Opaque()(S.TaggedStruct(tag, fields))
154
+ Object.assign(RequestClass, {
155
+ _tag: tag,
156
+ success: successSchema,
157
+ error: failureSchema,
158
+ ...(finalSchema !== undefined ? { final: finalSchema } : {}),
159
+ config: restConfig
160
+ })
161
+
162
+ return RequestClass
163
+ }
164
+
165
+ function makeTaggedRequestWithMeta<
166
+ ModuleName extends string,
167
+ Type extends "command" | "query"
168
+ >(
169
+ moduleName: ModuleName,
170
+ type: Type
171
+ ) {
172
+ function TaggedRequestWithMeta<Self, Resources extends InvalidationResources = never>(): {
173
+ // ─── stream: true overloads (must come before non-stream so TypeScript picks them) ───────────
174
+ <
175
+ Tag extends string,
176
+ Payload extends S.Struct.Fields,
177
+ Success extends S.Top | S.Struct.Fields,
178
+ Error extends S.Top | S.Struct.Fields,
179
+ Final extends S.Top | S.Struct.Fields = never,
180
+ C extends RequestConfig & Record<string, any> = RequestConfig & Record<string, any>
181
+ >(
182
+ tag: Tag,
183
+ fields: Payload,
184
+ config:
185
+ & Omit<C, "invalidatesQueries">
186
+ & { stream: true; success: Success; error: Error; final?: Final },
187
+ invalidatesQueries?: InvalidationCallback<
188
+ Resources,
189
+ InputFromPayload<Payload>,
190
+ OutputFromSuccess<SchemaOrFields<Success>>,
191
+ S.Schema.Type<ErrorResult<C & { success: Success; error: Error }>>
192
+ >
193
+ ): TaggedRequestForResult<
194
+ Self,
195
+ Tag,
196
+ Payload,
197
+ SchemaOrFields<Success>,
198
+ ErrorResult<C & { success: Success; error: Error }>,
199
+ Omit<
200
+ & Omit<C, "invalidatesQueries">
201
+ & {
202
+ success: Success
203
+ error: Error
204
+ }
205
+ & Partial<
206
+ InvalidationConfigForCommand<
207
+ Resources,
208
+ Payload,
209
+ SchemaOrFields<Success>,
210
+ ErrorResult<C & { success: Success; error: Error }>
211
+ >
212
+ >,
213
+ "success" | "error" | "stream"
214
+ >,
215
+ ModuleName,
216
+ Type,
217
+ true,
218
+ Resources,
219
+ [Final] extends [never] ? never : SchemaOrFields<Final>
220
+ >
221
+ <
222
+ Tag extends string,
223
+ Payload extends S.Struct.Fields,
224
+ Success extends S.Top | S.Struct.Fields,
225
+ Final extends S.Top | S.Struct.Fields = never,
226
+ C extends RequestConfig & Record<string, any> & { error?: never } =
227
+ & RequestConfig
228
+ & Record<string, any>
229
+ & { error?: never }
230
+ >(
231
+ tag: Tag,
232
+ fields: Payload,
233
+ config:
234
+ & Omit<C, "invalidatesQueries">
235
+ & { stream: true; success: Success; final?: Final },
236
+ invalidatesQueries?: InvalidationCallback<
237
+ Resources,
238
+ InputFromPayload<Payload>,
239
+ OutputFromSuccess<SchemaOrFields<Success>>,
240
+ S.Schema.Type<ErrorResult<C & { success: Success }>>
241
+ >
242
+ ): TaggedRequestForResult<
243
+ Self,
244
+ Tag,
245
+ Payload,
246
+ SchemaOrFields<Success>,
247
+ ErrorResult<C & { success: Success }>,
248
+ Omit<
249
+ & Omit<C, "invalidatesQueries">
250
+ & { success: Success }
251
+ & Partial<
252
+ InvalidationConfigForCommand<
253
+ Resources,
254
+ Payload,
255
+ SchemaOrFields<Success>,
256
+ ErrorResult<C & { success: Success }>
257
+ >
258
+ >,
259
+ "success" | "error" | "stream"
260
+ >,
261
+ ModuleName,
262
+ Type,
263
+ true,
264
+ Resources,
265
+ [Final] extends [never] ? never : SchemaOrFields<Final>
266
+ >
267
+ // ─── stream: true without `success` overloads ────────────────────────────────────────────────
268
+ <
269
+ Tag extends string,
270
+ Payload extends S.Struct.Fields,
271
+ Error extends S.Top | S.Struct.Fields,
272
+ C extends RequestConfig & Record<string, any> & { success?: never } =
273
+ & RequestConfig
274
+ & Record<string, any>
275
+ & { success?: never }
276
+ >(
277
+ tag: Tag,
278
+ fields: Payload,
279
+ config:
280
+ & Omit<C, "invalidatesQueries">
281
+ & { stream: true; error: Error },
282
+ invalidatesQueries?: InvalidationCallback<
283
+ Resources,
284
+ InputFromPayload<Payload>,
285
+ void,
286
+ S.Schema.Type<ErrorResult<C & { error: Error }>>
287
+ >
288
+ ): TaggedRequestForResult<
289
+ Self,
290
+ Tag,
291
+ Payload,
292
+ typeof ForceVoid,
293
+ ErrorResult<C & { error: Error }>,
294
+ Omit<
295
+ & Omit<C, "invalidatesQueries">
296
+ & { error: Error }
297
+ & Partial<
298
+ InvalidationConfigForCommand<
299
+ Resources,
300
+ Payload,
301
+ typeof ForceVoid,
302
+ ErrorResult<C & { error: Error }>
303
+ >
304
+ >,
305
+ "success" | "error" | "stream"
306
+ >,
307
+ ModuleName,
308
+ Type,
309
+ true,
310
+ Resources
311
+ >
312
+ <
313
+ Tag extends string,
314
+ Payload extends S.Struct.Fields,
315
+ C extends RequestConfig & Record<string, any> & { success?: never; error?: never } =
316
+ & RequestConfig
317
+ & Record<string, any>
318
+ & { success?: never; error?: never }
319
+ >(
320
+ tag: Tag,
321
+ fields: Payload,
322
+ config:
323
+ & Omit<C, "invalidatesQueries">
324
+ & { stream: true },
325
+ invalidatesQueries?: InvalidationCallback<
326
+ Resources,
327
+ InputFromPayload<Payload>,
328
+ void,
329
+ S.Schema.Type<ErrorResult<C>>
330
+ >
331
+ ): TaggedRequestForResult<
332
+ Self,
333
+ Tag,
334
+ Payload,
335
+ typeof ForceVoid,
336
+ ErrorResult<C>,
337
+ Omit<
338
+ & Omit<C, "invalidatesQueries">
339
+ & Partial<InvalidationConfigForCommand<Resources, Payload, typeof ForceVoid, ErrorResult<C>>>,
340
+ "success" | "error" | "stream"
341
+ >,
342
+ ModuleName,
343
+ Type,
344
+ true,
345
+ Resources
346
+ >
347
+ // ─── non-stream overloads ────────────────────────────────────────────────────────────────────
348
+ <
349
+ Tag extends string,
350
+ Payload extends S.Struct.Fields,
351
+ Success extends S.Top | S.Struct.Fields,
352
+ Error extends S.Top | S.Struct.Fields,
353
+ Final extends S.Top | S.Struct.Fields = never,
354
+ C extends RequestConfig & Record<string, any> = RequestConfig & Record<string, any>
355
+ >(
356
+ tag: Tag,
357
+ fields: Payload,
358
+ config:
359
+ & Omit<C, "invalidatesQueries">
360
+ & { success: Success; error: Error; final?: Final },
361
+ invalidatesQueries?: InvalidationCallback<
362
+ Resources,
363
+ InputFromPayload<Payload>,
364
+ OutputFromSuccess<SchemaOrFields<Success>>,
365
+ S.Schema.Type<ErrorResult<C & { success: Success; error: Error }>>
366
+ >
367
+ ): TaggedRequestForResult<
368
+ Self,
369
+ Tag,
370
+ Payload,
371
+ SchemaOrFields<Success>,
372
+ ErrorResult<C & { success: Success; error: Error }>,
373
+ Omit<
374
+ & Omit<C, "invalidatesQueries">
375
+ & {
376
+ success: Success
377
+ error: Error
378
+ }
379
+ & Partial<
380
+ InvalidationConfigForCommand<
381
+ Resources,
382
+ Payload,
383
+ SchemaOrFields<Success>,
384
+ ErrorResult<C & { success: Success; error: Error }>
385
+ >
386
+ >,
387
+ "success" | "error" | "stream"
388
+ >,
389
+ ModuleName,
390
+ Type,
391
+ false,
392
+ Resources,
393
+ [Final] extends [never] ? never : SchemaOrFields<Final>
394
+ >
395
+ <
396
+ Tag extends string,
397
+ Payload extends S.Struct.Fields,
398
+ Success extends S.Top | S.Struct.Fields,
399
+ Final extends S.Top | S.Struct.Fields = never,
400
+ C extends RequestConfig & Record<string, any> & { error?: never } =
401
+ & RequestConfig
402
+ & Record<string, any>
403
+ & {
404
+ error?: never
405
+ }
406
+ >(
407
+ tag: Tag,
408
+ fields: Payload,
409
+ config:
410
+ & Omit<C, "invalidatesQueries">
411
+ & { success: Success; final?: Final },
412
+ invalidatesQueries?: InvalidationCallback<
413
+ Resources,
414
+ InputFromPayload<Payload>,
415
+ OutputFromSuccess<SchemaOrFields<Success>>,
416
+ S.Schema.Type<ErrorResult<C & { success: Success }>>
417
+ >
418
+ ): TaggedRequestForResult<
419
+ Self,
420
+ Tag,
421
+ Payload,
422
+ SchemaOrFields<Success>,
423
+ ErrorResult<C & { success: Success }>,
424
+ Omit<
425
+ & Omit<C, "invalidatesQueries">
426
+ & {
427
+ success: Success
103
428
  }
104
- ;(this as any)._tag = tag
105
- }
106
- }
107
-
108
- Object.assign(RequestClass, payloadSchema, {
109
- _tag: tag,
110
- fields: taggedFields,
111
- success: successSchema,
112
- error: failureSchema,
113
- config
114
- })
115
-
116
- return RequestClass
117
- }) as any
429
+ & Partial<
430
+ InvalidationConfigForCommand<
431
+ Resources,
432
+ Payload,
433
+ SchemaOrFields<Success>,
434
+ ErrorResult<C & { success: Success }>
435
+ >
436
+ >,
437
+ "success" | "error" | "stream"
438
+ >,
439
+ ModuleName,
440
+ Type,
441
+ false,
442
+ Resources,
443
+ [Final] extends [never] ? never : SchemaOrFields<Final>
444
+ >
445
+ <
446
+ Tag extends string,
447
+ Payload extends S.Struct.Fields,
448
+ Error extends S.Top | S.Struct.Fields,
449
+ C extends RequestConfig & Record<string, any> & { success?: never }
450
+ >(
451
+ tag: Tag,
452
+ fields: Payload,
453
+ config:
454
+ & Omit<C, "invalidatesQueries">
455
+ & { error: Error },
456
+ invalidatesQueries?: InvalidationCallback<
457
+ Resources,
458
+ InputFromPayload<Payload>,
459
+ void,
460
+ S.Schema.Type<ErrorResult<C & { error: Error }>>
461
+ >
462
+ ): TaggedRequestForResult<
463
+ Self,
464
+ Tag,
465
+ Payload,
466
+ typeof ForceVoid,
467
+ ErrorResult<C & { error: Error }>,
468
+ Omit<
469
+ & Omit<C, "invalidatesQueries">
470
+ & {
471
+ error: Error
472
+ }
473
+ & Partial<
474
+ InvalidationConfigForCommand<
475
+ Resources,
476
+ Payload,
477
+ typeof ForceVoid,
478
+ ErrorResult<C & { error: Error }>
479
+ >
480
+ >,
481
+ "success" | "error" | "stream"
482
+ >,
483
+ ModuleName,
484
+ Type,
485
+ false,
486
+ Resources
487
+ >
488
+ <
489
+ Tag extends string,
490
+ Payload extends S.Struct.Fields,
491
+ C extends RequestConfig & Record<string, any> & { success?: never; error?: never }
492
+ >(
493
+ tag: Tag,
494
+ fields: Payload,
495
+ config: Omit<C, "invalidatesQueries">,
496
+ invalidatesQueries?: InvalidationCallback<
497
+ Resources,
498
+ InputFromPayload<Payload>,
499
+ void,
500
+ S.Schema.Type<ErrorResult<C>>
501
+ >
502
+ ): TaggedRequestForResult<
503
+ Self,
504
+ Tag,
505
+ Payload,
506
+ typeof ForceVoid,
507
+ ErrorResult<C>,
508
+ Omit<
509
+ & Omit<C, "invalidatesQueries">
510
+ & Partial<InvalidationConfigForCommand<Resources, Payload, typeof ForceVoid, ErrorResult<C>>>,
511
+ "success" | "error" | "stream"
512
+ >,
513
+ ModuleName,
514
+ Type,
515
+ false,
516
+ Resources
517
+ >
518
+ <Tag extends string, Payload extends S.Struct.Fields>(
519
+ tag: Tag,
520
+ fields: Payload
521
+ ): TaggedRequestForResult<
522
+ Self,
523
+ Tag,
524
+ Payload,
525
+ typeof ForceVoid,
526
+ ErrorResult<{}>,
527
+ Record<string, never>,
528
+ ModuleName,
529
+ Type,
530
+ false
531
+ >
532
+ } {
533
+ return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
534
+ tag: Tag,
535
+ fields: Fields,
536
+ config?: C,
537
+ invalidatesQueries?: InvalidationCallback<Resources>
538
+ ) => {
539
+ const isStream = (config as any)?.stream === true
540
+ const requestConfig = invalidatesQueries === undefined ? config : { ...config, invalidatesQueries }
541
+ const cls = makeRequestClass(tag, fields, requestConfig)
542
+ Object.assign(cls, { id: `${moduleName}.${tag}`, moduleName, type, stream: isStream })
543
+ return cls
544
+ }) as any
545
+ }
546
+ return Object.assign(TaggedRequestWithMeta, { moduleName, type } as const)
547
+ }
548
+
549
+ function TaggedRequestFor<ModuleName extends string>(moduleName: ModuleName) {
550
+ const Query = makeTaggedRequestWithMeta(moduleName, "query")
551
+ const Command = makeTaggedRequestWithMeta(moduleName, "command")
552
+
553
+ return {
554
+ moduleName,
555
+ /**
556
+ * Create query request classes for this module.
557
+ * Queries read state and should not mutate server state.
558
+ * Pass `stream: true` in the config to produce a Stream of `success` values (QueryStream behaviour).
559
+ */
560
+ Query,
561
+ /**
562
+ * Create command request classes for this module.
563
+ * Commands mutate state and should avoid returning complex read models.
564
+ * Pass `stream: true` in the config to produce a Stream of `success` values (CommandStream behaviour).
565
+ */
566
+ Command
567
+ } as const
118
568
  }
119
569
 
120
570
  return {
121
- TaggedRequest
571
+ TaggedRequestFor
122
572
  }
123
573
  }
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
  ))