effect 4.0.0-beta.97 → 4.0.0-beta.98

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 (166) hide show
  1. package/dist/LayerRef.d.ts +4 -6
  2. package/dist/LayerRef.d.ts.map +1 -1
  3. package/dist/LayerRef.js +4 -6
  4. package/dist/LayerRef.js.map +1 -1
  5. package/dist/Number.d.ts.map +1 -1
  6. package/dist/Number.js +28 -10
  7. package/dist/Number.js.map +1 -1
  8. package/dist/Schema.d.ts +22 -22
  9. package/dist/Schema.d.ts.map +1 -1
  10. package/dist/Schema.js +36 -33
  11. package/dist/Schema.js.map +1 -1
  12. package/dist/SchemaAST.d.ts.map +1 -1
  13. package/dist/SchemaAST.js +25 -10
  14. package/dist/SchemaAST.js.map +1 -1
  15. package/dist/SchemaError.d.ts +53 -0
  16. package/dist/SchemaError.d.ts.map +1 -0
  17. package/dist/SchemaError.js +61 -0
  18. package/dist/SchemaError.js.map +1 -0
  19. package/dist/SchemaGetter.d.ts.map +1 -1
  20. package/dist/SchemaGetter.js +17 -12
  21. package/dist/SchemaGetter.js.map +1 -1
  22. package/dist/SchemaRepresentation.d.ts.map +1 -1
  23. package/dist/SchemaRepresentation.js +186 -224
  24. package/dist/SchemaRepresentation.js.map +1 -1
  25. package/dist/index.d.ts +8 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +8 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/internal/effect.js +26 -8
  30. package/dist/internal/effect.js.map +1 -1
  31. package/dist/internal/schema/equivalence.js +1 -2
  32. package/dist/internal/schema/equivalence.js.map +1 -1
  33. package/dist/internal/schema/representation.js +1 -3
  34. package/dist/internal/schema/representation.js.map +1 -1
  35. package/dist/internal/schema/schema.d.ts +0 -13
  36. package/dist/internal/schema/schema.d.ts.map +1 -1
  37. package/dist/internal/schema/schema.js +4 -29
  38. package/dist/internal/schema/schema.js.map +1 -1
  39. package/dist/unstable/ai/McpSchema.d.ts +128 -128
  40. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  41. package/dist/unstable/cli/CliError.d.ts +8 -16
  42. package/dist/unstable/cli/CliError.d.ts.map +1 -1
  43. package/dist/unstable/cli/CliError.js +8 -16
  44. package/dist/unstable/cli/CliError.js.map +1 -1
  45. package/dist/unstable/cli/Command.d.ts +4 -3
  46. package/dist/unstable/cli/Command.d.ts.map +1 -1
  47. package/dist/unstable/cli/Command.js +3 -3
  48. package/dist/unstable/cli/Command.js.map +1 -1
  49. package/dist/unstable/cli/GlobalFlag.d.ts +6 -8
  50. package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -1
  51. package/dist/unstable/cli/GlobalFlag.js +6 -6
  52. package/dist/unstable/cli/GlobalFlag.js.map +1 -1
  53. package/dist/unstable/cluster/EntityProxyServer.d.ts +1 -1
  54. package/dist/unstable/cluster/EntityProxyServer.d.ts.map +1 -1
  55. package/dist/unstable/cluster/EntityProxyServer.js +2 -4
  56. package/dist/unstable/cluster/EntityProxyServer.js.map +1 -1
  57. package/dist/unstable/cluster/internal/entityManager.js +15 -12
  58. package/dist/unstable/cluster/internal/entityManager.js.map +1 -1
  59. package/dist/unstable/httpapi/HttpApi.d.ts +36 -15
  60. package/dist/unstable/httpapi/HttpApi.d.ts.map +1 -1
  61. package/dist/unstable/httpapi/HttpApi.js +19 -20
  62. package/dist/unstable/httpapi/HttpApi.js.map +1 -1
  63. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +51 -62
  64. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  65. package/dist/unstable/httpapi/HttpApiBuilder.js +68 -58
  66. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  67. package/dist/unstable/httpapi/HttpApiClient.d.ts +53 -53
  68. package/dist/unstable/httpapi/HttpApiClient.d.ts.map +1 -1
  69. package/dist/unstable/httpapi/HttpApiClient.js +40 -30
  70. package/dist/unstable/httpapi/HttpApiClient.js.map +1 -1
  71. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts +165 -164
  72. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts.map +1 -1
  73. package/dist/unstable/httpapi/HttpApiEndpoint.js +31 -19
  74. package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
  75. package/dist/unstable/httpapi/HttpApiError.d.ts +22 -0
  76. package/dist/unstable/httpapi/HttpApiError.d.ts.map +1 -1
  77. package/dist/unstable/httpapi/HttpApiError.js +31 -0
  78. package/dist/unstable/httpapi/HttpApiError.js.map +1 -1
  79. package/dist/unstable/httpapi/HttpApiGroup.d.ts +55 -30
  80. package/dist/unstable/httpapi/HttpApiGroup.d.ts.map +1 -1
  81. package/dist/unstable/httpapi/HttpApiGroup.js +22 -29
  82. package/dist/unstable/httpapi/HttpApiGroup.js.map +1 -1
  83. package/dist/unstable/httpapi/HttpApiMiddleware.d.ts +8 -8
  84. package/dist/unstable/httpapi/HttpApiMiddleware.d.ts.map +1 -1
  85. package/dist/unstable/httpapi/HttpApiMiddleware.js.map +1 -1
  86. package/dist/unstable/httpapi/HttpApiScalar.d.ts +2 -2
  87. package/dist/unstable/httpapi/HttpApiScalar.d.ts.map +1 -1
  88. package/dist/unstable/httpapi/HttpApiScalar.js +4 -3
  89. package/dist/unstable/httpapi/HttpApiScalar.js.map +1 -1
  90. package/dist/unstable/httpapi/HttpApiSchema.d.ts +8 -0
  91. package/dist/unstable/httpapi/HttpApiSchema.d.ts.map +1 -1
  92. package/dist/unstable/httpapi/HttpApiSchema.js.map +1 -1
  93. package/dist/unstable/httpapi/HttpApiSwagger.d.ts +1 -1
  94. package/dist/unstable/httpapi/HttpApiSwagger.d.ts.map +1 -1
  95. package/dist/unstable/httpapi/HttpApiSwagger.js.map +1 -1
  96. package/dist/unstable/httpapi/HttpApiTest.d.ts +4 -4
  97. package/dist/unstable/httpapi/HttpApiTest.d.ts.map +1 -1
  98. package/dist/unstable/httpapi/HttpApiTest.js +9 -8
  99. package/dist/unstable/httpapi/HttpApiTest.js.map +1 -1
  100. package/dist/unstable/httpapi/OpenApi.d.ts +1 -1
  101. package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
  102. package/dist/unstable/httpapi/OpenApi.js +77 -37
  103. package/dist/unstable/httpapi/OpenApi.js.map +1 -1
  104. package/dist/unstable/httpapi/internal/html.js +6 -2
  105. package/dist/unstable/httpapi/internal/html.js.map +1 -1
  106. package/dist/unstable/httpapi/internal/mediaType.d.ts +2 -0
  107. package/dist/unstable/httpapi/internal/mediaType.d.ts.map +1 -0
  108. package/dist/unstable/httpapi/internal/mediaType.js +7 -0
  109. package/dist/unstable/httpapi/internal/mediaType.js.map +1 -0
  110. package/dist/unstable/persistence/PersistedQueue.d.ts.map +1 -1
  111. package/dist/unstable/persistence/PersistedQueue.js +16 -12
  112. package/dist/unstable/persistence/PersistedQueue.js.map +1 -1
  113. package/dist/unstable/reactivity/Atom.js +1 -1
  114. package/dist/unstable/reactivity/Atom.js.map +1 -1
  115. package/dist/unstable/reactivity/AtomHttpApi.d.ts +7 -7
  116. package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
  117. package/dist/unstable/reactivity/AtomHttpApi.js +3 -2
  118. package/dist/unstable/reactivity/AtomHttpApi.js.map +1 -1
  119. package/dist/unstable/rpc/RpcServer.d.ts +3 -1
  120. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  121. package/dist/unstable/rpc/RpcServer.js +6 -3
  122. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  123. package/dist/unstable/workflow/DurableDeferred.js +1 -1
  124. package/dist/unstable/workflow/DurableDeferred.js.map +1 -1
  125. package/dist/unstable/workflow/WorkflowProxyServer.d.ts +1 -1
  126. package/dist/unstable/workflow/WorkflowProxyServer.d.ts.map +1 -1
  127. package/dist/unstable/workflow/WorkflowProxyServer.js +1 -2
  128. package/dist/unstable/workflow/WorkflowProxyServer.js.map +1 -1
  129. package/package.json +1 -1
  130. package/src/LayerRef.ts +4 -6
  131. package/src/Number.ts +29 -10
  132. package/src/Schema.ts +42 -34
  133. package/src/SchemaAST.ts +25 -13
  134. package/src/SchemaError.ts +64 -0
  135. package/src/SchemaGetter.ts +22 -16
  136. package/src/SchemaRepresentation.ts +165 -202
  137. package/src/index.ts +10 -0
  138. package/src/internal/effect.ts +39 -22
  139. package/src/internal/schema/equivalence.ts +1 -2
  140. package/src/internal/schema/representation.ts +4 -4
  141. package/src/internal/schema/schema.ts +4 -37
  142. package/src/unstable/cli/CliError.ts +24 -16
  143. package/src/unstable/cli/Command.ts +8 -8
  144. package/src/unstable/cli/GlobalFlag.ts +22 -33
  145. package/src/unstable/cluster/EntityProxyServer.ts +15 -17
  146. package/src/unstable/cluster/internal/entityManager.ts +48 -34
  147. package/src/unstable/httpapi/HttpApi.ts +72 -47
  148. package/src/unstable/httpapi/HttpApiBuilder.ts +320 -217
  149. package/src/unstable/httpapi/HttpApiClient.ts +196 -151
  150. package/src/unstable/httpapi/HttpApiEndpoint.ts +251 -447
  151. package/src/unstable/httpapi/HttpApiError.ts +33 -0
  152. package/src/unstable/httpapi/HttpApiGroup.ts +95 -74
  153. package/src/unstable/httpapi/HttpApiMiddleware.ts +10 -7
  154. package/src/unstable/httpapi/HttpApiScalar.ts +15 -12
  155. package/src/unstable/httpapi/HttpApiSchema.ts +8 -0
  156. package/src/unstable/httpapi/HttpApiSwagger.ts +2 -2
  157. package/src/unstable/httpapi/HttpApiTest.ts +15 -14
  158. package/src/unstable/httpapi/OpenApi.ts +74 -36
  159. package/src/unstable/httpapi/internal/html.ts +9 -2
  160. package/src/unstable/httpapi/internal/mediaType.ts +6 -0
  161. package/src/unstable/persistence/PersistedQueue.ts +16 -14
  162. package/src/unstable/reactivity/Atom.ts +1 -1
  163. package/src/unstable/reactivity/AtomHttpApi.ts +164 -162
  164. package/src/unstable/rpc/RpcServer.ts +10 -4
  165. package/src/unstable/workflow/DurableDeferred.ts +1 -1
  166. package/src/unstable/workflow/WorkflowProxyServer.ts +11 -12
@@ -27,6 +27,7 @@ const notAcceptableResponse = HttpServerResponse.empty({ status: 406 })
27
27
  const requestTimeoutResponse = HttpServerResponse.empty({ status: 408 })
28
28
  const conflictResponse = HttpServerResponse.empty({ status: 409 })
29
29
  const goneResponse = HttpServerResponse.empty({ status: 410 })
30
+ const unprocessableEntityResponse = HttpServerResponse.empty({ status: 422 })
30
31
  const internalServerErrorResponse = HttpServerResponse.empty({ status: 500 })
31
32
  const notImplementedResponse = HttpServerResponse.empty({ status: 501 })
32
33
  const serviceUnavailableResponse = HttpServerResponse.empty({ status: 503 })
@@ -302,6 +303,38 @@ export const GoneNoContent = Gone.pipe(HttpApiSchema.asNoContent({
302
303
  decode: () => new Gone({})
303
304
  }))
304
305
 
306
+ /**
307
+ * Built-in HTTP API error for a `422 Unprocessable Entity` response. When used
308
+ * directly as a server response, it renders as an empty response with status 422.
309
+ *
310
+ * @category errors
311
+ * @since 4.0.0
312
+ */
313
+ export class UnprocessableEntity
314
+ extends Schema.ErrorClass<UnprocessableEntity>("effect/HttpApiError/UnprocessableEntity")({
315
+ _tag: Schema.tag("UnprocessableEntity")
316
+ }, {
317
+ description: "UnprocessableEntity",
318
+ httpApiStatus: 422
319
+ })
320
+ {
321
+ override readonly [ErrorReporter.ignore] = true;
322
+ [HttpServerRespondable.symbol]() {
323
+ return Effect.succeed(unprocessableEntityResponse)
324
+ }
325
+ }
326
+
327
+ /**
328
+ * No-content schema variant for `UnprocessableEntity`, decoding an empty 422
329
+ * response into an `UnprocessableEntity` error value.
330
+ *
331
+ * @category NoContent errors
332
+ * @since 4.0.0
333
+ */
334
+ export const UnprocessableEntityNoContent = UnprocessableEntity.pipe(HttpApiSchema.asNoContent({
335
+ decode: () => new UnprocessableEntity({})
336
+ }))
337
+
305
338
  /**
306
339
  * Built-in HTTP API error for a `500 Internal Server Error` response. When used
307
340
  * directly as a server response, it renders as an empty response with status 500.
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import type { NonEmptyReadonlyArray } from "../../Array.ts"
14
14
  import * as Context from "../../Context.ts"
15
+ import * as internalRecord from "../../internal/record.ts"
15
16
  import { type Pipeable, pipeArguments } from "../../Pipeable.ts"
16
17
  import * as Predicate from "../../Predicate.ts"
17
18
  import * as Record from "../../Record.ts"
@@ -28,7 +29,14 @@ const TypeId = "~effect/httpapi/HttpApiGroup"
28
29
  * @category guards
29
30
  * @since 4.0.0
30
31
  */
31
- export const isHttpApiGroup = (u: unknown): u is Any => Predicate.hasProperty(u, TypeId)
32
+ export const isHttpApiGroup = (u: unknown): u is Top => Predicate.hasProperty(u, TypeId)
33
+
34
+ /**
35
+ * Endpoints indexed by their identifier.
36
+ */
37
+ type EndpointMap<Endpoints extends HttpApiEndpoint.Constraint> = {
38
+ readonly [Endpoint in Endpoints as HttpApiEndpoint.Identifier<Endpoint>]: Endpoint
39
+ }
32
40
 
33
41
  /**
34
42
  * An `HttpApiGroup` is a named collection of `HttpApiEndpoint`s that represents
@@ -43,21 +51,26 @@ export const isHttpApiGroup = (u: unknown): u is Any => Predicate.hasProperty(u,
43
51
  */
44
52
  export interface HttpApiGroup<
45
53
  out Id extends string,
46
- out Endpoints extends HttpApiEndpoint.Any = never,
54
+ in out Endpoints extends HttpApiEndpoint.Constraint = never,
47
55
  out TopLevel extends boolean = false
48
56
  > extends Pipeable {
49
57
  new(_: never): {}
50
58
  readonly [TypeId]: typeof TypeId
59
+ /**
60
+ * Stable group identifier. This field intentionally is not named `name`
61
+ * because `HttpApiGroup` values can be extended as classes, where `name`
62
+ * would collide with JavaScript's built-in `Function.name`.
63
+ */
51
64
  readonly identifier: Id
52
65
  readonly key: string
53
66
  readonly topLevel: TopLevel
54
- readonly endpoints: Record.ReadonlyRecord<string, Endpoints>
67
+ readonly endpoints: EndpointMap<Endpoints>
55
68
  readonly annotations: Context.Context<never>
56
69
 
57
70
  /**
58
71
  * Add an `HttpApiEndpoint` to an `HttpApiGroup`.
59
72
  */
60
- add<const A extends NonEmptyReadonlyArray<HttpApiEndpoint.Any>>(
73
+ add<const A extends NonEmptyReadonlyArray<HttpApiEndpoint.Constraint>>(
61
74
  ...endpoints: A
62
75
  ): HttpApiGroup<Id, Endpoints | A[number], TopLevel>
63
76
 
@@ -113,50 +126,63 @@ export interface HttpApiGroup<
113
126
  }
114
127
 
115
128
  /**
116
- * Type-level identity for a group within an HTTP API, pairing the API id with the
117
- * group name for service derivation.
129
+ * Type-level service produced by the layer that implements one group of an HTTP
130
+ * API.
131
+ *
132
+ * **Details**
133
+ *
134
+ * `HttpApiBuilder.group` provides this service, and `HttpApiBuilder.layer`
135
+ * requires one service for each group in the API. The type carries both the API
136
+ * id and the group identifier so the relationship between an API and its
137
+ * implemented groups is checked at compile time.
118
138
  *
119
139
  * @category models
120
140
  * @since 4.0.0
121
141
  */
122
- export interface ApiGroup<ApiId extends string, Name extends string> {
142
+ export interface Service<ApiId extends string, Identifier extends string> {
123
143
  readonly _: unique symbol
124
144
  readonly apiId: ApiId
125
- readonly name: Name
145
+ readonly identifier: Identifier
126
146
  }
127
147
 
128
148
  /**
129
- * A widened `HttpApiGroup` type used when the concrete group name, endpoints, and
130
- * top-level flag are not needed.
149
+ * Derives the group implementation service required for each group in an HTTP
150
+ * API.
151
+ *
152
+ * **Details**
153
+ *
154
+ * When given an API id and a group or union of groups, this type maps each group
155
+ * to the `Service` identity that must be provided by `HttpApiBuilder.group`.
131
156
  *
132
157
  * @category models
133
158
  * @since 4.0.0
134
159
  */
135
- export interface Any {
136
- readonly [TypeId]: typeof TypeId
137
- readonly identifier: string
138
- readonly key: string
139
- readonly endpoints: Record.ReadonlyRecord<string, HttpApiEndpoint.Any>
140
- }
160
+ export type ToService<ApiId extends string, Group extends Constraint> = Group extends Constraint ?
161
+ Service<ApiId, Group["identifier"]>
162
+ : never
141
163
 
142
164
  /**
143
- * A widened group type that preserves concrete runtime properties such as
144
- * identifier, key, top-level status, endpoints, and annotations.
165
+ * A widened `HttpApiGroup` type used when the concrete group identifier,
166
+ * endpoints, and top-level flag are not needed.
145
167
  *
146
168
  * @category models
147
169
  * @since 4.0.0
148
170
  */
149
- export type AnyWithProps = HttpApiGroup<string, HttpApiEndpoint.AnyWithProps, boolean>
171
+ export interface Constraint {
172
+ readonly [TypeId]: typeof TypeId
173
+ readonly identifier: string
174
+ readonly key: string
175
+ readonly endpoints: Record.ReadonlyRecord<string, HttpApiEndpoint.Constraint>
176
+ }
150
177
 
151
178
  /**
152
- * Derives the API-specific `ApiGroup` service identity for an HTTP API group.
179
+ * A widened group type that preserves concrete runtime properties such as
180
+ * identifier, key, top-level status, endpoints, and annotations.
153
181
  *
154
182
  * @category models
155
183
  * @since 4.0.0
156
184
  */
157
- export type ToService<ApiId extends string, A> = A extends HttpApiGroup<infer Name, infer _Endpoints, infer _TopLevel> ?
158
- ApiGroup<ApiId, Name>
159
- : never
185
+ export interface Top extends HttpApiGroup<string, HttpApiEndpoint.Top, boolean> {}
160
186
 
161
187
  /**
162
188
  * Selects the group with the specified identifier from a union of groups.
@@ -164,7 +190,7 @@ export type ToService<ApiId extends string, A> = A extends HttpApiGroup<infer Na
164
190
  * @category models
165
191
  * @since 4.0.0
166
192
  */
167
- export type WithName<Group, Name extends string> = Extract<Group, { readonly identifier: Name }>
193
+ export type WithIdentifier<Group, Identifier extends string> = Extract<Group, { readonly identifier: Identifier }>
168
194
 
169
195
  /**
170
196
  * Extracts the identifier literal from an `HttpApiGroup`.
@@ -172,8 +198,7 @@ export type WithName<Group, Name extends string> = Extract<Group, { readonly ide
172
198
  * @category models
173
199
  * @since 4.0.0
174
200
  */
175
- export type Name<Group> = Group extends HttpApiGroup<infer _Name, infer _Endpoints, infer _TopLevel> ? _Name
176
- : never
201
+ export type Identifier<Group> = Group extends Constraint ? Group["identifier"] : never
177
202
 
178
203
  /**
179
204
  * Extracts the endpoint union contained in an `HttpApiGroup`.
@@ -181,7 +206,8 @@ export type Name<Group> = Group extends HttpApiGroup<infer _Name, infer _Endpoin
181
206
  * @category models
182
207
  * @since 4.0.0
183
208
  */
184
- export type Endpoints<Group> = Group extends HttpApiGroup<infer _Name, infer _Endpoints, infer _TopLevel> ? _Endpoints
209
+ export type Endpoints<Group> = Group extends HttpApiGroup<infer _Identifier, infer _Endpoints, infer _TopLevel> ?
210
+ _Endpoints
185
211
  : never
186
212
 
187
213
  /**
@@ -236,12 +262,14 @@ export type MiddlewareClient<Group> = HttpApiEndpoint.MiddlewareClient<Endpoints
236
262
  export type MiddlewareServices<Group> = HttpApiEndpoint.MiddlewareServices<Endpoints<Group>>
237
263
 
238
264
  /**
239
- * Selects the endpoints in a group whose `name` matches the provided endpoint name.
265
+ * Extracts the endpoint union from the group with the specified identifier.
240
266
  *
241
267
  * @category models
242
268
  * @since 4.0.0
243
269
  */
244
- export type EndpointsWithName<Group extends Any, Name extends string> = Endpoints<WithName<Group, Name>>
270
+ export type EndpointsWithIdentifier<Group extends Constraint, Identifier extends string> = Endpoints<
271
+ WithIdentifier<Group, Identifier>
272
+ >
245
273
 
246
274
  /**
247
275
  * Computes the schema encoding and decoding services required by clients for all endpoints in a group.
@@ -249,7 +277,7 @@ export type EndpointsWithName<Group extends Any, Name extends string> = Endpoint
249
277
  * @category models
250
278
  * @since 4.0.0
251
279
  */
252
- export type ClientServices<Group> = Group extends HttpApiGroup<infer _Name, infer _Endpoints, infer _TopLevel> ?
280
+ export type ClientServices<Group> = Group extends HttpApiGroup<infer _Identifier, infer _Endpoints, infer _TopLevel> ?
253
281
  HttpApiEndpoint.ClientServices<_Endpoints>
254
282
  : never
255
283
 
@@ -260,8 +288,8 @@ export type ClientServices<Group> = Group extends HttpApiGroup<infer _Name, infe
260
288
  * @since 4.0.0
261
289
  */
262
290
  export type AddPrefix<Group, Prefix extends PathInput> = Group extends
263
- HttpApiGroup<infer _Name, infer _Endpoints, infer _TopLevel> ?
264
- HttpApiGroup<_Name, HttpApiEndpoint.AddPrefix<_Endpoints, Prefix>, _TopLevel>
291
+ HttpApiGroup<infer _Identifier, infer _Endpoints, infer _TopLevel> ?
292
+ HttpApiGroup<_Identifier, HttpApiEndpoint.AddPrefix<_Endpoints, Prefix>, _TopLevel>
265
293
  : never
266
294
 
267
295
  /**
@@ -271,70 +299,56 @@ export type AddPrefix<Group, Prefix extends PathInput> = Group extends
271
299
  * @since 4.0.0
272
300
  */
273
301
  export type AddMiddleware<Group, Id extends HttpApiMiddleware.AnyId> = Group extends
274
- HttpApiGroup<infer _Name, infer _Endpoints, infer _TopLevel> ?
275
- HttpApiGroup<_Name, HttpApiEndpoint.AddMiddleware<_Endpoints, Id>, _TopLevel>
302
+ HttpApiGroup<infer _Identifier, infer _Endpoints, infer _TopLevel> ?
303
+ HttpApiGroup<_Identifier, HttpApiEndpoint.AddMiddleware<_Endpoints, Id>, _TopLevel>
276
304
  : never
277
305
 
278
306
  const Proto = {
279
307
  [TypeId]: TypeId,
280
- add(this: AnyWithProps, ...toAdd: NonEmptyReadonlyArray<HttpApiEndpoint.AnyWithProps>) {
308
+ add(this: Top, ...toAdd: NonEmptyReadonlyArray<HttpApiEndpoint.Top>) {
281
309
  const endpoints = { ...this.endpoints }
282
310
  for (const endpoint of toAdd) {
283
- endpoints[endpoint.name] = endpoint
311
+ internalRecord.set(endpoints, endpoint.identifier, endpoint)
284
312
  }
285
313
  return makeProto({
286
- identifier: this.identifier,
287
- topLevel: this.topLevel,
288
- endpoints,
289
- annotations: this.annotations
314
+ ...optionsFromGroup(this),
315
+ endpoints
290
316
  })
291
317
  },
292
- prefix(this: AnyWithProps, prefix: PathInput) {
318
+ prefix(this: Top, prefix: PathInput) {
293
319
  return makeProto({
294
- identifier: this.identifier,
295
- topLevel: this.topLevel,
296
- endpoints: Record.map(this.endpoints, (endpoint) => endpoint.prefix(prefix)),
297
- annotations: this.annotations
320
+ ...optionsFromGroup(this),
321
+ endpoints: Record.map(this.endpoints, (endpoint) => endpoint.prefix(prefix))
298
322
  })
299
323
  },
300
- middleware(this: AnyWithProps, middleware: HttpApiMiddleware.AnyService) {
324
+ middleware(this: Top, middleware: HttpApiMiddleware.AnyService) {
301
325
  return makeProto({
302
- identifier: this.identifier,
303
- topLevel: this.topLevel,
304
- endpoints: Record.map(this.endpoints, (endpoint) => endpoint.middleware(middleware as any)),
305
- annotations: this.annotations
326
+ ...optionsFromGroup(this),
327
+ endpoints: Record.map(this.endpoints, (endpoint) => endpoint.middleware(middleware as any))
306
328
  })
307
329
  },
308
- annotateMerge<I>(this: AnyWithProps, annotations: Context.Context<I>) {
330
+ annotateMerge<I>(this: Top, annotations: Context.Context<I>) {
309
331
  return makeProto({
310
- identifier: this.identifier,
311
- topLevel: this.topLevel,
312
- endpoints: this.endpoints,
332
+ ...optionsFromGroup(this),
313
333
  annotations: Context.merge(this.annotations, annotations)
314
334
  })
315
335
  },
316
- annotate<I, S>(this: AnyWithProps, annotation: Context.Key<I, S>, value: S) {
336
+ annotate<I, S>(this: Top, annotation: Context.Key<I, S>, value: S) {
317
337
  return makeProto({
318
- identifier: this.identifier,
319
- topLevel: this.topLevel,
320
- endpoints: this.endpoints,
338
+ ...optionsFromGroup(this),
321
339
  annotations: Context.add(this.annotations, annotation, value)
322
340
  })
323
341
  },
324
- annotateEndpointsMerge<I>(this: AnyWithProps, annotations: Context.Context<I>) {
342
+ annotateEndpointsMerge<I>(this: Top, annotations: Context.Context<I>) {
325
343
  return makeProto({
326
- identifier: this.identifier,
327
- topLevel: this.topLevel,
328
- endpoints: Record.map(this.endpoints, (endpoint) => endpoint.annotateMerge(annotations)),
329
- annotations: this.annotations
344
+ ...optionsFromGroup(this),
345
+ endpoints: Record.map(this.endpoints, (endpoint) => endpoint.annotateMerge(annotations))
330
346
  })
331
347
  },
332
- annotateEndpoints<I, S>(this: AnyWithProps, annotation: Context.Key<I, S>, value: S) {
348
+ annotateEndpoints<I, S>(this: Top, annotation: Context.Key<I, S>, value: S) {
333
349
  return makeProto({
334
- identifier: this.identifier,
335
- topLevel: this.topLevel,
336
- endpoints: Record.map(this.endpoints, (endpoint) => endpoint.annotate(annotation, value)),
337
- annotations: this.annotations
350
+ ...optionsFromGroup(this),
351
+ endpoints: Record.map(this.endpoints, (endpoint) => endpoint.annotate(annotation, value))
338
352
  })
339
353
  },
340
354
  pipe() {
@@ -342,14 +356,21 @@ const Proto = {
342
356
  }
343
357
  }
344
358
 
359
+ const optionsFromGroup = (group: Top) => ({
360
+ identifier: group.identifier,
361
+ topLevel: group.topLevel,
362
+ endpoints: group.endpoints,
363
+ annotations: group.annotations
364
+ })
365
+
345
366
  const makeProto = <
346
367
  Id extends string,
347
- Endpoints extends HttpApiEndpoint.Any,
368
+ Endpoints extends HttpApiEndpoint.Constraint,
348
369
  TopLevel extends (true | false)
349
370
  >(options: {
350
371
  readonly identifier: Id
351
372
  readonly topLevel: TopLevel
352
- readonly endpoints: Record.ReadonlyRecord<string, Endpoints>
373
+ readonly endpoints: Record.ReadonlyRecord<string, HttpApiEndpoint.Constraint>
353
374
  readonly annotations: Context.Context<never>
354
375
  }): HttpApiGroup<Id, Endpoints, TopLevel> => {
355
376
  function HttpApiGroup() {}
@@ -365,7 +386,7 @@ const makeProto = <
365
386
  *
366
387
  * Add endpoints with `add`, provide implementations with `HttpApiBuilder.group`,
367
388
  * and set `topLevel` when the generated client should expose endpoint methods
368
- * directly instead of nesting them under the group name.
389
+ * directly instead of nesting them under the group identifier.
369
390
  *
370
391
  * @category constructors
371
392
  * @since 4.0.0
@@ -375,7 +396,7 @@ export const make = <const Id extends string, const TopLevel extends boolean = f
375
396
  }): HttpApiGroup<Id, never, TopLevel> =>
376
397
  makeProto({
377
398
  identifier,
378
- topLevel: options?.topLevel ?? false as any,
379
- endpoints: Record.empty(),
399
+ topLevel: (options?.topLevel ?? false) as TopLevel,
400
+ endpoints: {},
380
401
  annotations: Context.empty()
381
- }) as any
402
+ })
@@ -64,8 +64,8 @@ type ErrorSchemaFromConstraint<E> = E extends ReadonlyArray<Schema.Constraint> ?
64
64
  export type HttpApiMiddleware<Provides, E extends ErrorConstraint, Requires> = (
65
65
  httpEffect: Effect.Effect<HttpServerResponse, unhandled, Provides>,
66
66
  options: {
67
- readonly endpoint: HttpApiEndpoint.AnyWithProps
68
- readonly group: HttpApiGroup.AnyWithProps
67
+ readonly endpoint: HttpApiEndpoint.Top
68
+ readonly group: HttpApiGroup.Top
69
69
  }
70
70
  ) => Effect.Effect<HttpServerResponse, unhandled | ErrorSchemaFromConstraint<E>["Type"], Requires | HttpRouter.Provided>
71
71
 
@@ -90,8 +90,8 @@ export type HttpApiMiddlewareSecurity<
90
90
  httpEffect: Effect.Effect<HttpServerResponse, unhandled, Provides>,
91
91
  options: {
92
92
  readonly credential: HttpApiSecurity.HttpApiSecurity.Type<Security[K]>
93
- readonly endpoint: HttpApiEndpoint.AnyWithProps
94
- readonly group: HttpApiGroup.AnyWithProps
93
+ readonly endpoint: HttpApiEndpoint.Top
94
+ readonly group: HttpApiGroup.Top
95
95
  }
96
96
  ) => Effect.Effect<
97
97
  HttpServerResponse,
@@ -113,8 +113,8 @@ export type HttpApiMiddlewareSecurity<
113
113
  */
114
114
  export interface HttpApiMiddlewareClient<_E, CE, R> {
115
115
  (options: {
116
- readonly endpoint: HttpApiEndpoint.AnyWithProps
117
- readonly group: HttpApiGroup.AnyWithProps
116
+ readonly endpoint: HttpApiEndpoint.Top
117
+ readonly group: HttpApiGroup.Top
118
118
  readonly request: HttpClientRequest.HttpClientRequest
119
119
  readonly next: (
120
120
  request: HttpClientRequest.HttpClientRequest
@@ -420,7 +420,10 @@ export const layerSchemaErrorTransform = <Id, E extends ErrorConstraint, Require
420
420
  service: Context.Service<Id, HttpApiMiddleware<never, E, Requires>>,
421
421
  transform: (
422
422
  error: HttpApiSchemaError,
423
- context: { readonly endpoint: HttpApiEndpoint.AnyWithProps; readonly group: HttpApiGroup.AnyWithProps }
423
+ context: {
424
+ readonly endpoint: HttpApiEndpoint.Top
425
+ readonly group: HttpApiGroup.Top
426
+ }
424
427
  ) => Effect.Effect<
425
428
  HttpServerResponse,
426
429
  ErrorSchemaFromConstraint<E>["Type"] | HttpApiSchemaError,
@@ -151,7 +151,7 @@ type ScalarSource =
151
151
  readonly source: string
152
152
  }
153
153
 
154
- const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options: {
154
+ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Constraint>(options: {
155
155
  readonly api: HttpApi.HttpApi<Id, Groups>
156
156
  readonly source: ScalarSource
157
157
  readonly scalar: ScalarConfig | undefined
@@ -162,6 +162,15 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
162
162
  _integration: "html",
163
163
  ...scalar
164
164
  }
165
+ const scalarScript = options.source._tag === "Cdn"
166
+ ? `<script src="${
167
+ Html.escapeAttribute(
168
+ `https://cdn.jsdelivr.net/npm/@scalar/api-reference@${
169
+ encodeURIComponent(options.source.version ?? "latest")
170
+ }/dist/browser/standalone.min.js`
171
+ )
172
+ }" crossorigin></script>`
173
+ : `<script>${options.source.source}</script>`
165
174
  const response = HttpServerResponse.html(`<!doctype html>
166
175
  <html>
167
176
  <head>
@@ -170,12 +179,12 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
170
179
  ${
171
180
  !spec.info.description
172
181
  ? ""
173
- : `<meta name="description" content="${Html.escape(spec.info.description)}"/>`
182
+ : `<meta name="description" content="${Html.escapeAttribute(spec.info.description)}"/>`
174
183
  }
175
184
  ${
176
185
  !spec.info.description
177
186
  ? ""
178
- : `<meta name="og:description" content="${Html.escape(spec.info.description)}"/>`
187
+ : `<meta name="og:description" content="${Html.escapeAttribute(spec.info.description)}"/>`
179
188
  }
180
189
  <meta
181
190
  name="viewport"
@@ -183,13 +192,7 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
183
192
  </head>
184
193
  <body>
185
194
  <div id="api-reference-container"></div>
186
- ${
187
- options.source._tag === "Cdn"
188
- ? `<script src="${`https://cdn.jsdelivr.net/npm/@scalar/api-reference@${
189
- options.source.version ?? "latest"
190
- }/dist/browser/standalone.min.js`}" crossorigin></script>`
191
- : `<script>${options.source.source}</script>`
192
- }
195
+ ${scalarScript}
193
196
  <script>
194
197
  window.Scalar.createApiReference(document.getElementById('api-reference-container'), {
195
198
  ...${Html.escapeJson(scalarConfig)},
@@ -215,7 +218,7 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
215
218
  * @category layers
216
219
  * @since 4.0.0
217
220
  */
218
- export const layer = <Id extends string, Groups extends HttpApiGroup.Any>(
221
+ export const layer = <Id extends string, Groups extends HttpApiGroup.Constraint>(
219
222
  api: HttpApi.HttpApi<Id, Groups>,
220
223
  options?: {
221
224
  readonly path?: `/${string}` | undefined
@@ -246,7 +249,7 @@ export const layer = <Id extends string, Groups extends HttpApiGroup.Any>(
246
249
  * @category layers
247
250
  * @since 4.0.0
248
251
  */
249
- export const layerCdn = <Id extends string, Groups extends HttpApiGroup.Any>(
252
+ export const layerCdn = <Id extends string, Groups extends HttpApiGroup.Constraint>(
250
253
  api: HttpApi.HttpApi<Id, Groups>,
251
254
  options?: {
252
255
  readonly path?: `/${string}` | undefined
@@ -287,6 +287,14 @@ export type StreamSseMode = "events" | "data"
287
287
  * constructed from `data`, handlers and clients expose raw data values while
288
288
  * the server and client still use an SSE event schema internally.
289
289
  *
290
+ * **Gotchas**
291
+ *
292
+ * The client treats `effect/httpapi/stream/failure` as a stream failure only
293
+ * when its decoded `data` is a `Cause`. If an event schema accepts that name
294
+ * dynamically but decodes `data` to another value, the client emits it as an
295
+ * application event. Endpoint construction rejects event schemas that declare
296
+ * the reserved name statically.
297
+ *
290
298
  * @category models
291
299
  * @since 4.0.0
292
300
  */
@@ -18,7 +18,7 @@ import * as Html from "./internal/html.ts"
18
18
  import * as internal from "./internal/httpApiSwagger.ts"
19
19
  import * as OpenApi from "./OpenApi.ts"
20
20
 
21
- const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options: {
21
+ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Constraint>(options: {
22
22
  readonly api: HttpApi.HttpApi<Id, Groups>
23
23
  }) => {
24
24
  const spec = OpenApi.fromApi(options.api)
@@ -56,7 +56,7 @@ const makeHandler = <Id extends string, Groups extends HttpApiGroup.Any>(options
56
56
  * @category layers
57
57
  * @since 4.0.0
58
58
  */
59
- export const layer = <Id extends string, Groups extends HttpApiGroup.Any>(
59
+ export const layer = <Id extends string, Groups extends HttpApiGroup.Constraint>(
60
60
  api: HttpApi.HttpApi<Id, Groups>,
61
61
  options?: {
62
62
  readonly path?: `/${string}` | undefined
@@ -21,7 +21,7 @@ import * as HttpRouter from "../http/HttpRouter.ts"
21
21
  import * as HttpServerRequest from "../http/HttpServerRequest.ts"
22
22
  import * as HttpServerResponse from "../http/HttpServerResponse.ts"
23
23
  import type * as HttpApi from "./HttpApi.ts"
24
- import type { Handlers } from "./HttpApiBuilder.ts"
24
+ import type { HandlerRuntime } from "./HttpApiBuilder.ts"
25
25
  import * as HttpApiBuilder from "./HttpApiBuilder.ts"
26
26
  import * as HttpApiClient from "./HttpApiClient.ts"
27
27
  import type * as HttpApiEndpoint from "./HttpApiEndpoint.ts"
@@ -40,12 +40,12 @@ import type * as HttpApiGroup from "./HttpApiGroup.ts"
40
40
  */
41
41
  export const groups = Effect.fnUntraced(function*<
42
42
  ApiId extends string,
43
- Groups extends HttpApiGroup.Any,
44
- const Names extends ReadonlyArray<HttpApiGroup.Name<Groups>>,
45
- SelectedGroups = HttpApiGroup.WithName<Groups, Names[number]>
43
+ Groups extends HttpApiGroup.Constraint,
44
+ const Identifiers extends ReadonlyArray<HttpApiGroup.Identifier<Groups>>,
45
+ SelectedGroups extends HttpApiGroup.Constraint = HttpApiGroup.WithIdentifier<Groups, Identifiers[number]>
46
46
  >(
47
47
  api: HttpApi.HttpApi<ApiId, Groups>,
48
- groupNames: Names,
48
+ groupIdentifiers: Identifiers,
49
49
  options?: {
50
50
  readonly baseUrl?: string | URL | undefined
51
51
  }
@@ -63,22 +63,23 @@ export const groups = Effect.fnUntraced(function*<
63
63
  > {
64
64
  let context = yield* Effect.context<HttpApiGroup.ToService<ApiId, SelectedGroups>>()
65
65
 
66
- for (const name in api.groups) {
67
- const group = api.groups[name]
68
- if (groupNames.includes(name as any)) {
66
+ const groups = api.groups as unknown as Record<string, HttpApiGroup.Top>
67
+ for (const identifier in groups) {
68
+ const group = groups[identifier]
69
+ if (groupIdentifiers.includes(identifier as any)) {
69
70
  continue
70
71
  }
71
- const handlers = new Map<string, Handlers.Item<never>>()
72
+ const handlers = new Map<string, HandlerRuntime>()
72
73
  const routes: Array<HttpRouter.Route<any, any>> = []
73
- for (const endpointName in group.endpoints) {
74
- const endpoint = group.endpoints[endpointName]
75
- const handler: Handlers.Item<never> = {
74
+ for (const endpointIdentifier in group.endpoints) {
75
+ const endpoint = group.endpoints[endpointIdentifier]
76
+ const handler: HandlerRuntime = {
76
77
  endpoint: endpoint as any,
77
- handler: () => Effect.die(new Error(`Unhandled endpoint: ${endpointName}`)),
78
+ handler: () => Effect.die(new Error(`Unhandled endpoint: ${endpointIdentifier}`)),
78
79
  isRaw: false,
79
80
  uninterruptible: false
80
81
  }
81
- handlers.set(endpointName, handler)
82
+ handlers.set(endpointIdentifier, handler)
82
83
  routes.push(HttpApiBuilder.handlerToRoute(group as any, handler, context))
83
84
  }
84
85
  context = Context.add(context, group as any, { handlers, routes })