@effect/platform 0.70.7 → 0.71.1

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 (54) hide show
  1. package/README.md +22 -34
  2. package/dist/cjs/HttpApi.js +15 -6
  3. package/dist/cjs/HttpApi.js.map +1 -1
  4. package/dist/cjs/HttpApiBuilder.js +4 -4
  5. package/dist/cjs/HttpApiBuilder.js.map +1 -1
  6. package/dist/cjs/HttpApiClient.js.map +1 -1
  7. package/dist/cjs/HttpApiError.js +109 -1
  8. package/dist/cjs/HttpApiError.js.map +1 -1
  9. package/dist/cjs/HttpApiGroup.js.map +1 -1
  10. package/dist/cjs/HttpApiSchema.js +46 -2
  11. package/dist/cjs/HttpApiSchema.js.map +1 -1
  12. package/dist/cjs/HttpApp.js +6 -3
  13. package/dist/cjs/HttpApp.js.map +1 -1
  14. package/dist/cjs/OpenApi.js +19 -2
  15. package/dist/cjs/OpenApi.js.map +1 -1
  16. package/dist/dts/HttpApi.d.ts +13 -12
  17. package/dist/dts/HttpApi.d.ts.map +1 -1
  18. package/dist/dts/HttpApiBuilder.d.ts +9 -9
  19. package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
  20. package/dist/dts/HttpApiClient.d.ts +3 -3
  21. package/dist/dts/HttpApiClient.d.ts.map +1 -1
  22. package/dist/dts/HttpApiError.d.ts +85 -0
  23. package/dist/dts/HttpApiError.d.ts.map +1 -1
  24. package/dist/dts/HttpApiGroup.d.ts +3 -2
  25. package/dist/dts/HttpApiGroup.d.ts.map +1 -1
  26. package/dist/dts/HttpApiSchema.d.ts +19 -1
  27. package/dist/dts/HttpApiSchema.d.ts.map +1 -1
  28. package/dist/dts/HttpApp.d.ts +8 -4
  29. package/dist/dts/HttpApp.d.ts.map +1 -1
  30. package/dist/dts/OpenApi.d.ts +8 -0
  31. package/dist/dts/OpenApi.d.ts.map +1 -1
  32. package/dist/esm/HttpApi.js +13 -5
  33. package/dist/esm/HttpApi.js.map +1 -1
  34. package/dist/esm/HttpApiBuilder.js +4 -4
  35. package/dist/esm/HttpApiBuilder.js.map +1 -1
  36. package/dist/esm/HttpApiClient.js.map +1 -1
  37. package/dist/esm/HttpApiError.js +96 -0
  38. package/dist/esm/HttpApiError.js.map +1 -1
  39. package/dist/esm/HttpApiGroup.js.map +1 -1
  40. package/dist/esm/HttpApiSchema.js +45 -2
  41. package/dist/esm/HttpApiSchema.js.map +1 -1
  42. package/dist/esm/HttpApp.js +6 -3
  43. package/dist/esm/HttpApp.js.map +1 -1
  44. package/dist/esm/OpenApi.js +16 -0
  45. package/dist/esm/OpenApi.js.map +1 -1
  46. package/package.json +2 -2
  47. package/src/HttpApi.ts +35 -19
  48. package/src/HttpApiBuilder.ts +22 -20
  49. package/src/HttpApiClient.ts +8 -6
  50. package/src/HttpApiError.ts +108 -0
  51. package/src/HttpApiGroup.ts +4 -3
  52. package/src/HttpApiSchema.ts +63 -5
  53. package/src/HttpApp.ts +18 -6
  54. package/src/OpenApi.ts +18 -1
@@ -2,9 +2,12 @@
2
2
  * @since 1.0.0
3
3
  */
4
4
  import type { Brand } from "effect/Brand"
5
+ import * as Effect from "effect/Effect"
6
+ import * as Effectable from "effect/Effectable"
5
7
  import type { LazyArg } from "effect/Function"
6
- import { constVoid, dual } from "effect/Function"
8
+ import { constant, constVoid, dual } from "effect/Function"
7
9
  import { globalValue } from "effect/GlobalValue"
10
+ import { hasProperty } from "effect/Predicate"
8
11
  import * as Schema from "effect/Schema"
9
12
  import * as AST from "effect/SchemaAST"
10
13
  import * as Struct from "effect/Struct"
@@ -245,7 +248,7 @@ export const asEmpty: {
245
248
  self: S,
246
249
  options: {
247
250
  readonly status: number
248
- readonly decode?: LazyArg<Schema.Schema.Type<S>>
251
+ readonly decode: LazyArg<Schema.Schema.Type<S>>
249
252
  }
250
253
  ): asEmpty<S>
251
254
  } = dual(
@@ -254,14 +257,14 @@ export const asEmpty: {
254
257
  self: S,
255
258
  options: {
256
259
  readonly status: number
257
- readonly decode?: LazyArg<Schema.Schema.Type<S>>
260
+ readonly decode: LazyArg<Schema.Schema.Type<S>>
258
261
  }
259
262
  ): asEmpty<S> =>
260
263
  Schema.transform(
261
- Schema.Void,
264
+ Schema.Void.annotations(self.ast.annotations),
262
265
  Schema.typeSchema(self),
263
266
  {
264
- decode: options.decode as any,
267
+ decode: options.decode,
265
268
  encode: constVoid
266
269
  }
267
270
  ).annotations(annotations({
@@ -482,3 +485,58 @@ export const deunionize = (
482
485
  schemas.add(schema)
483
486
  }
484
487
  }
488
+
489
+ /**
490
+ * @since 1.0.0
491
+ * @category empty errors
492
+ */
493
+ export interface EmptyErrorClass<Self, Tag> extends Schema.Schema<Self, void> {
494
+ new(_: void): { readonly _tag: Tag } & Effect.Effect<never, Self>
495
+ }
496
+
497
+ /**
498
+ * @since 1.0.0
499
+ * @category empty errors
500
+ */
501
+ export const EmptyError = <Self>() =>
502
+ <const Tag extends string>(options: {
503
+ readonly tag: Tag
504
+ readonly status: number
505
+ }): EmptyErrorClass<Self, Tag> => {
506
+ const symbol = Symbol.for(`@effect/platform/HttpApiSchema/EmptyError/${options.tag}`)
507
+ class EmptyError extends Effectable.StructuralClass<never, Self> {
508
+ readonly _tag: Tag = options.tag
509
+ commit(): Effect.Effect<never, Self> {
510
+ return Effect.fail(this) as any
511
+ }
512
+ }
513
+ ;(EmptyError as any).prototype[symbol] = symbol
514
+ Object.assign(EmptyError, {
515
+ [Schema.TypeId]: Schema.Void[Schema.TypeId],
516
+ pipe: Schema.Void.pipe,
517
+ annotations(this: any, annotations: any) {
518
+ return Schema.make(this.ast).annotations(annotations)
519
+ }
520
+ })
521
+ let transform: Schema.Schema.Any | undefined
522
+ Object.defineProperty(EmptyError, "ast", {
523
+ get() {
524
+ if (transform) {
525
+ return transform.ast
526
+ }
527
+ const self = this as any
528
+ transform = asEmpty(
529
+ Schema.declare((u) => hasProperty(u, symbol), {
530
+ identifier: options.tag,
531
+ title: options.tag
532
+ }),
533
+ {
534
+ status: options.status,
535
+ decode: constant(new self())
536
+ }
537
+ )
538
+ return transform.ast
539
+ }
540
+ })
541
+ return EmptyError as any
542
+ }
package/src/HttpApp.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  import * as Context from "effect/Context"
5
5
  import * as Effect from "effect/Effect"
6
6
  import * as Exit from "effect/Exit"
7
- import type * as FiberRef from "effect/FiberRef"
7
+ import * as FiberRef from "effect/FiberRef"
8
8
  import * as Layer from "effect/Layer"
9
9
  import type * as Option from "effect/Option"
10
10
  import * as Runtime from "effect/Runtime"
@@ -160,11 +160,20 @@ export const toWebHandlerRuntime = <R>(runtime: Runtime.Runtime<R>) => {
160
160
  )
161
161
  return Effect.void
162
162
  }, middleware)
163
- return (request: Request): Promise<Response> =>
163
+ return (request: Request, context?: Context.Context<never> | undefined): Promise<Response> =>
164
164
  new Promise((resolve) => {
165
+ const contextMap = new Map<string, any>(
166
+ context ?
167
+ [
168
+ ...runtime.context.unsafeMap,
169
+ ...context.unsafeMap
170
+ ] :
171
+ runtime.context.unsafeMap
172
+ )
165
173
  const httpServerRequest = ServerRequest.fromWeb(request)
174
+ contextMap.set(ServerRequest.HttpServerRequest.key, httpServerRequest)
166
175
  ;(httpServerRequest as any)[resolveSymbol] = resolve
167
- const fiber = run(Effect.provideService(httpApp, ServerRequest.HttpServerRequest, httpServerRequest))
176
+ const fiber = run(Effect.locally(httpApp as any, FiberRef.currentContext, Context.unsafeMake(contextMap)))
168
177
  request.signal?.addEventListener("abort", () => {
169
178
  fiber.unsafeInterruptAsFork(ServerError.clientAbortFiberId)
170
179
  }, { once: true })
@@ -179,7 +188,9 @@ export const toWebHandlerRuntime = <R>(runtime: Runtime.Runtime<R>) => {
179
188
  export const toWebHandler: <E>(
180
189
  self: Default<E, Scope.Scope>,
181
190
  middleware?: HttpMiddleware | undefined
182
- ) => (request: Request) => Promise<Response> = toWebHandlerRuntime(Runtime.defaultRuntime)
191
+ ) => (request: Request, context?: Context.Context<never> | undefined) => Promise<Response> = toWebHandlerRuntime(
192
+ Runtime.defaultRuntime
193
+ )
183
194
 
184
195
  /**
185
196
  * @since 1.0.0
@@ -191,12 +202,13 @@ export const toWebHandlerLayer = <E, R, RE>(
191
202
  middleware?: HttpMiddleware | undefined
192
203
  ): {
193
204
  readonly close: () => Promise<void>
194
- readonly handler: (request: Request) => Promise<Response>
205
+ readonly handler: (request: Request, context?: Context.Context<never> | undefined) => Promise<Response>
195
206
  } => {
196
207
  const scope = Effect.runSync(Scope.make())
197
208
  const close = () => Effect.runPromise(Scope.close(scope, Exit.void))
198
209
  const build = Effect.map(Layer.toRuntime(layer), (_) => toWebHandlerRuntime(_)(self, middleware))
199
210
  const runner = Effect.runPromise(Scope.extend(build, scope))
200
- const handler = (request: Request): Promise<Response> => runner.then((handler) => handler(request))
211
+ const handler = (request: Request, context?: Context.Context<never> | undefined): Promise<Response> =>
212
+ runner.then((handler) => handler(request, context))
201
213
  return { close, handler } as const
202
214
  }
package/src/OpenApi.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * @since 1.0.0
3
3
  */
4
4
  import * as Context from "effect/Context"
5
+ import { constFalse } from "effect/Function"
5
6
  import { globalValue } from "effect/GlobalValue"
6
7
  import * as Option from "effect/Option"
7
8
  import type { ReadonlyRecord } from "effect/Record"
@@ -84,6 +85,14 @@ export class Deprecated extends Context.Tag("@effect/platform/OpenApi/Deprecated
84
85
  */
85
86
  export class Override extends Context.Tag("@effect/platform/OpenApi/Override")<Override, Record<string, unknown>>() {}
86
87
 
88
+ /**
89
+ * @since 1.0.0
90
+ * @category annotations
91
+ */
92
+ export class Exclude extends Context.Reference<Exclude>()("@effect/platform/OpenApi/Exclude", {
93
+ defaultValue: constFalse
94
+ }) {}
95
+
87
96
  /**
88
97
  * Transforms the generated OpenAPI specification
89
98
  * @since 1.0.0
@@ -127,6 +136,7 @@ export const annotations: (
127
136
  readonly servers?: ReadonlyArray<OpenAPISpecServer> | undefined
128
137
  readonly format?: string | undefined
129
138
  readonly override?: Record<string, unknown> | undefined
139
+ readonly exclude?: boolean | undefined
130
140
  readonly transform?: ((openApiSpec: Record<string, any>) => Record<string, any>) | undefined
131
141
  }
132
142
  ) => Context.Context<never> = contextPartial({
@@ -140,6 +150,7 @@ export const annotations: (
140
150
  servers: Servers,
141
151
  format: Format,
142
152
  override: Override,
153
+ exclude: Exclude,
143
154
  transform: Transform
144
155
  })
145
156
 
@@ -213,6 +224,9 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
213
224
  })
214
225
  HttpApi.reflect(api as any, {
215
226
  onGroup({ group }) {
227
+ if (Context.get(group.annotations, Exclude)) {
228
+ return
229
+ }
216
230
  let tag: Mutable<OpenAPISpecTag> = {
217
231
  name: Context.getOrElse(group.annotations, Title, () => group.identifier)
218
232
  }
@@ -230,7 +244,10 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
230
244
  })
231
245
  spec.tags!.push(tag)
232
246
  },
233
- onEndpoint({ endpoint, errors, group, middleware, payloads, successes }) {
247
+ onEndpoint({ endpoint, errors, group, mergedAnnotations, middleware, payloads, successes }) {
248
+ if (Context.get(mergedAnnotations, Exclude)) {
249
+ return
250
+ }
234
251
  const path = endpoint.path.replace(/:(\w+)[^/]*/g, "{$1}")
235
252
  const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
236
253
  let op: DeepMutable<OpenAPISpecOperation> = {