@effect/platform 0.68.6 → 0.69.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 (229) hide show
  1. package/HttpApiMiddleware/package.json +6 -0
  2. package/README.md +306 -233
  3. package/dist/cjs/Headers.js +7 -2
  4. package/dist/cjs/Headers.js.map +1 -1
  5. package/dist/cjs/HttpApi.js +90 -78
  6. package/dist/cjs/HttpApi.js.map +1 -1
  7. package/dist/cjs/HttpApiBuilder.js +245 -255
  8. package/dist/cjs/HttpApiBuilder.js.map +1 -1
  9. package/dist/cjs/HttpApiClient.js +64 -59
  10. package/dist/cjs/HttpApiClient.js.map +1 -1
  11. package/dist/cjs/HttpApiEndpoint.js +74 -109
  12. package/dist/cjs/HttpApiEndpoint.js.map +1 -1
  13. package/dist/cjs/HttpApiError.js +3 -4
  14. package/dist/cjs/HttpApiError.js.map +1 -1
  15. package/dist/cjs/HttpApiGroup.js +103 -100
  16. package/dist/cjs/HttpApiGroup.js.map +1 -1
  17. package/dist/cjs/HttpApiMiddleware.js +67 -0
  18. package/dist/cjs/HttpApiMiddleware.js.map +1 -0
  19. package/dist/cjs/HttpApiSchema.js +33 -7
  20. package/dist/cjs/HttpApiSchema.js.map +1 -1
  21. package/dist/cjs/HttpApiSecurity.js +2 -2
  22. package/dist/cjs/HttpApiSecurity.js.map +1 -1
  23. package/dist/cjs/HttpApiSwagger.js +3 -1
  24. package/dist/cjs/HttpApiSwagger.js.map +1 -1
  25. package/dist/cjs/HttpBody.js.map +1 -1
  26. package/dist/cjs/HttpIncomingMessage.js +5 -1
  27. package/dist/cjs/HttpIncomingMessage.js.map +1 -1
  28. package/dist/cjs/HttpServer.js +12 -1
  29. package/dist/cjs/HttpServer.js.map +1 -1
  30. package/dist/cjs/HttpServerRespondable.js +1 -1
  31. package/dist/cjs/HttpServerRespondable.js.map +1 -1
  32. package/dist/cjs/OpenApi.js +102 -63
  33. package/dist/cjs/OpenApi.js.map +1 -1
  34. package/dist/cjs/OpenApiJsonSchema.js +58 -47
  35. package/dist/cjs/OpenApiJsonSchema.js.map +1 -1
  36. package/dist/cjs/Transferable.js +2 -2
  37. package/dist/cjs/Transferable.js.map +1 -1
  38. package/dist/cjs/UrlParams.js +5 -1
  39. package/dist/cjs/UrlParams.js.map +1 -1
  40. package/dist/cjs/Worker.js.map +1 -1
  41. package/dist/cjs/WorkerError.js +1 -5
  42. package/dist/cjs/WorkerError.js.map +1 -1
  43. package/dist/cjs/WorkerRunner.js.map +1 -1
  44. package/dist/cjs/index.js +3 -1
  45. package/dist/cjs/internal/httpBody.js +1 -1
  46. package/dist/cjs/internal/httpBody.js.map +1 -1
  47. package/dist/cjs/internal/httpClientRequest.js.map +1 -1
  48. package/dist/cjs/internal/httpClientResponse.js +1 -1
  49. package/dist/cjs/internal/httpClientResponse.js.map +1 -1
  50. package/dist/cjs/internal/httpRouter.js +1 -1
  51. package/dist/cjs/internal/httpRouter.js.map +1 -1
  52. package/dist/cjs/internal/httpServer.js +7 -1
  53. package/dist/cjs/internal/httpServer.js.map +1 -1
  54. package/dist/cjs/internal/httpServerRequest.js +1 -1
  55. package/dist/cjs/internal/httpServerRequest.js.map +1 -1
  56. package/dist/cjs/internal/httpServerResponse.js.map +1 -1
  57. package/dist/cjs/internal/keyValueStore.js +1 -1
  58. package/dist/cjs/internal/keyValueStore.js.map +1 -1
  59. package/dist/cjs/internal/multipart.js +1 -1
  60. package/dist/cjs/internal/multipart.js.map +1 -1
  61. package/dist/cjs/internal/worker.js +6 -7
  62. package/dist/cjs/internal/worker.js.map +1 -1
  63. package/dist/cjs/internal/workerRunner.js +3 -4
  64. package/dist/cjs/internal/workerRunner.js.map +1 -1
  65. package/dist/dts/Headers.d.ts +4 -6
  66. package/dist/dts/Headers.d.ts.map +1 -1
  67. package/dist/dts/HttpApi.d.ts +64 -140
  68. package/dist/dts/HttpApi.d.ts.map +1 -1
  69. package/dist/dts/HttpApiBuilder.d.ts +86 -167
  70. package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
  71. package/dist/dts/HttpApiClient.d.ts +34 -11
  72. package/dist/dts/HttpApiClient.d.ts.map +1 -1
  73. package/dist/dts/HttpApiEndpoint.d.ts +119 -273
  74. package/dist/dts/HttpApiEndpoint.d.ts.map +1 -1
  75. package/dist/dts/HttpApiError.d.ts +5 -2
  76. package/dist/dts/HttpApiError.d.ts.map +1 -1
  77. package/dist/dts/HttpApiGroup.d.ts +96 -194
  78. package/dist/dts/HttpApiGroup.d.ts.map +1 -1
  79. package/dist/dts/HttpApiMiddleware.d.ts +228 -0
  80. package/dist/dts/HttpApiMiddleware.d.ts.map +1 -0
  81. package/dist/dts/HttpApiSchema.d.ts +6 -2
  82. package/dist/dts/HttpApiSchema.d.ts.map +1 -1
  83. package/dist/dts/HttpApiSecurity.d.ts +1 -1
  84. package/dist/dts/HttpApiSecurity.d.ts.map +1 -1
  85. package/dist/dts/HttpApiSwagger.d.ts +2 -2
  86. package/dist/dts/HttpApiSwagger.d.ts.map +1 -1
  87. package/dist/dts/HttpBody.d.ts +2 -2
  88. package/dist/dts/HttpBody.d.ts.map +1 -1
  89. package/dist/dts/HttpClientRequest.d.ts +2 -2
  90. package/dist/dts/HttpClientRequest.d.ts.map +1 -1
  91. package/dist/dts/HttpClientResponse.d.ts +3 -3
  92. package/dist/dts/HttpClientResponse.d.ts.map +1 -1
  93. package/dist/dts/HttpIncomingMessage.d.ts +3 -3
  94. package/dist/dts/HttpIncomingMessage.d.ts.map +1 -1
  95. package/dist/dts/HttpRouter.d.ts +3 -3
  96. package/dist/dts/HttpRouter.d.ts.map +1 -1
  97. package/dist/dts/HttpServer.d.ts +15 -0
  98. package/dist/dts/HttpServer.d.ts.map +1 -1
  99. package/dist/dts/HttpServerRequest.d.ts +3 -3
  100. package/dist/dts/HttpServerRequest.d.ts.map +1 -1
  101. package/dist/dts/HttpServerRespondable.d.ts.map +1 -1
  102. package/dist/dts/HttpServerResponse.d.ts +2 -2
  103. package/dist/dts/HttpServerResponse.d.ts.map +1 -1
  104. package/dist/dts/KeyValueStore.d.ts +2 -2
  105. package/dist/dts/KeyValueStore.d.ts.map +1 -1
  106. package/dist/dts/Multipart.d.ts +3 -3
  107. package/dist/dts/Multipart.d.ts.map +1 -1
  108. package/dist/dts/OpenApi.d.ts +17 -39
  109. package/dist/dts/OpenApi.d.ts.map +1 -1
  110. package/dist/dts/OpenApiJsonSchema.d.ts +10 -5
  111. package/dist/dts/OpenApiJsonSchema.d.ts.map +1 -1
  112. package/dist/dts/Transferable.d.ts +4 -1
  113. package/dist/dts/Transferable.d.ts.map +1 -1
  114. package/dist/dts/UrlParams.d.ts +3 -6
  115. package/dist/dts/UrlParams.d.ts.map +1 -1
  116. package/dist/dts/Worker.d.ts +7 -8
  117. package/dist/dts/Worker.d.ts.map +1 -1
  118. package/dist/dts/WorkerError.d.ts +1 -1
  119. package/dist/dts/WorkerError.d.ts.map +1 -1
  120. package/dist/dts/WorkerRunner.d.ts +2 -3
  121. package/dist/dts/WorkerRunner.d.ts.map +1 -1
  122. package/dist/dts/index.d.ts +4 -0
  123. package/dist/dts/index.d.ts.map +1 -1
  124. package/dist/dts/internal/httpRouter.d.ts.map +1 -1
  125. package/dist/esm/Headers.js +7 -2
  126. package/dist/esm/Headers.js.map +1 -1
  127. package/dist/esm/HttpApi.js +88 -77
  128. package/dist/esm/HttpApi.js.map +1 -1
  129. package/dist/esm/HttpApiBuilder.js +238 -244
  130. package/dist/esm/HttpApiBuilder.js.map +1 -1
  131. package/dist/esm/HttpApiClient.js +64 -59
  132. package/dist/esm/HttpApiClient.js.map +1 -1
  133. package/dist/esm/HttpApiEndpoint.js +73 -106
  134. package/dist/esm/HttpApiEndpoint.js.map +1 -1
  135. package/dist/esm/HttpApiError.js +3 -4
  136. package/dist/esm/HttpApiError.js.map +1 -1
  137. package/dist/esm/HttpApiGroup.js +102 -99
  138. package/dist/esm/HttpApiGroup.js.map +1 -1
  139. package/dist/esm/HttpApiMiddleware.js +56 -0
  140. package/dist/esm/HttpApiMiddleware.js.map +1 -0
  141. package/dist/esm/HttpApiSchema.js +31 -5
  142. package/dist/esm/HttpApiSchema.js.map +1 -1
  143. package/dist/esm/HttpApiSecurity.js +1 -1
  144. package/dist/esm/HttpApiSecurity.js.map +1 -1
  145. package/dist/esm/HttpApiSwagger.js +4 -2
  146. package/dist/esm/HttpApiSwagger.js.map +1 -1
  147. package/dist/esm/HttpBody.js.map +1 -1
  148. package/dist/esm/HttpIncomingMessage.js +4 -1
  149. package/dist/esm/HttpIncomingMessage.js.map +1 -1
  150. package/dist/esm/HttpServer.js +11 -0
  151. package/dist/esm/HttpServer.js.map +1 -1
  152. package/dist/esm/HttpServerRespondable.js +1 -1
  153. package/dist/esm/HttpServerRespondable.js.map +1 -1
  154. package/dist/esm/OpenApi.js +97 -59
  155. package/dist/esm/OpenApi.js.map +1 -1
  156. package/dist/esm/OpenApiJsonSchema.js +56 -46
  157. package/dist/esm/OpenApiJsonSchema.js.map +1 -1
  158. package/dist/esm/Transferable.js +2 -2
  159. package/dist/esm/Transferable.js.map +1 -1
  160. package/dist/esm/UrlParams.js +4 -1
  161. package/dist/esm/UrlParams.js.map +1 -1
  162. package/dist/esm/Worker.js.map +1 -1
  163. package/dist/esm/WorkerError.js +1 -4
  164. package/dist/esm/WorkerError.js.map +1 -1
  165. package/dist/esm/WorkerRunner.js.map +1 -1
  166. package/dist/esm/index.js +4 -0
  167. package/dist/esm/index.js.map +1 -1
  168. package/dist/esm/internal/httpBody.js +1 -1
  169. package/dist/esm/internal/httpBody.js.map +1 -1
  170. package/dist/esm/internal/httpClientRequest.js.map +1 -1
  171. package/dist/esm/internal/httpClientResponse.js +1 -1
  172. package/dist/esm/internal/httpClientResponse.js.map +1 -1
  173. package/dist/esm/internal/httpRouter.js +1 -1
  174. package/dist/esm/internal/httpRouter.js.map +1 -1
  175. package/dist/esm/internal/httpServer.js +6 -0
  176. package/dist/esm/internal/httpServer.js.map +1 -1
  177. package/dist/esm/internal/httpServerRequest.js +1 -1
  178. package/dist/esm/internal/httpServerRequest.js.map +1 -1
  179. package/dist/esm/internal/httpServerResponse.js.map +1 -1
  180. package/dist/esm/internal/keyValueStore.js +1 -1
  181. package/dist/esm/internal/keyValueStore.js.map +1 -1
  182. package/dist/esm/internal/multipart.js +1 -1
  183. package/dist/esm/internal/multipart.js.map +1 -1
  184. package/dist/esm/internal/worker.js +6 -7
  185. package/dist/esm/internal/worker.js.map +1 -1
  186. package/dist/esm/internal/workerRunner.js +3 -4
  187. package/dist/esm/internal/workerRunner.js.map +1 -1
  188. package/package.json +10 -3
  189. package/src/Headers.ts +12 -4
  190. package/src/HttpApi.ts +183 -258
  191. package/src/HttpApiBuilder.ts +534 -481
  192. package/src/HttpApiClient.ts +163 -112
  193. package/src/HttpApiEndpoint.ts +443 -564
  194. package/src/HttpApiError.ts +4 -6
  195. package/src/HttpApiGroup.ts +277 -325
  196. package/src/HttpApiMiddleware.ts +317 -0
  197. package/src/HttpApiSchema.ts +39 -2
  198. package/src/HttpApiSecurity.ts +1 -1
  199. package/src/HttpApiSwagger.ts +3 -3
  200. package/src/HttpBody.ts +2 -2
  201. package/src/HttpClientRequest.ts +2 -2
  202. package/src/HttpClientResponse.ts +3 -3
  203. package/src/HttpIncomingMessage.ts +3 -3
  204. package/src/HttpRouter.ts +3 -3
  205. package/src/HttpServer.ts +21 -0
  206. package/src/HttpServerRequest.ts +3 -3
  207. package/src/HttpServerRespondable.ts +1 -1
  208. package/src/HttpServerResponse.ts +2 -2
  209. package/src/KeyValueStore.ts +2 -2
  210. package/src/Multipart.ts +3 -3
  211. package/src/OpenApi.ts +113 -104
  212. package/src/OpenApiJsonSchema.ts +67 -53
  213. package/src/Transferable.ts +2 -2
  214. package/src/UrlParams.ts +3 -3
  215. package/src/Worker.ts +7 -8
  216. package/src/WorkerError.ts +1 -1
  217. package/src/WorkerRunner.ts +2 -3
  218. package/src/index.ts +5 -0
  219. package/src/internal/httpBody.ts +2 -2
  220. package/src/internal/httpClientRequest.ts +2 -2
  221. package/src/internal/httpClientResponse.ts +3 -3
  222. package/src/internal/httpRouter.ts +2 -2
  223. package/src/internal/httpServer.ts +13 -0
  224. package/src/internal/httpServerRequest.ts +3 -3
  225. package/src/internal/httpServerResponse.ts +2 -2
  226. package/src/internal/keyValueStore.ts +1 -1
  227. package/src/internal/multipart.ts +3 -3
  228. package/src/internal/worker.ts +6 -7
  229. package/src/internal/workerRunner.ts +3 -4
@@ -3,10 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.toWebHandler = exports.serve = exports.securitySetCookie = exports.securityDecode = exports.middlewareSecurityVoid = exports.middlewareSecurity = exports.middlewareOpenApi = exports.middlewareLayerScoped = exports.middlewareLayer = exports.middlewareCors = exports.middleware = exports.httpApp = exports.handle = exports.group = exports.api = exports.Router = exports.Middleware = exports.HandlersTypeId = void 0;
7
- var AST = _interopRequireWildcard(require("@effect/schema/AST"));
8
- var ParseResult = _interopRequireWildcard(require("@effect/schema/ParseResult"));
9
- var Schema = _interopRequireWildcard(require("@effect/schema/Schema"));
6
+ exports.toWebHandler = exports.serve = exports.securitySetCookie = exports.securityDecode = exports.middlewareScoped = exports.middlewareOpenApi = exports.middlewareCors = exports.middleware = exports.httpApp = exports.group = exports.api = exports.Router = exports.Middleware = exports.HandlersTypeId = void 0;
10
7
  var Chunk = _interopRequireWildcard(require("effect/Chunk"));
11
8
  var Context = _interopRequireWildcard(require("effect/Context"));
12
9
  var Effect = _interopRequireWildcard(require("effect/Effect"));
@@ -14,14 +11,19 @@ var Encoding = _interopRequireWildcard(require("effect/Encoding"));
14
11
  var FiberRef = _interopRequireWildcard(require("effect/FiberRef"));
15
12
  var _Function = require("effect/Function");
16
13
  var _GlobalValue = require("effect/GlobalValue");
14
+ var HashMap = _interopRequireWildcard(require("effect/HashMap"));
15
+ var HashSet = _interopRequireWildcard(require("effect/HashSet"));
17
16
  var Layer = _interopRequireWildcard(require("effect/Layer"));
17
+ var ManagedRuntime = _interopRequireWildcard(require("effect/ManagedRuntime"));
18
18
  var Option = _interopRequireWildcard(require("effect/Option"));
19
+ var ParseResult = _interopRequireWildcard(require("effect/ParseResult"));
19
20
  var _Pipeable = require("effect/Pipeable");
20
21
  var Redacted = _interopRequireWildcard(require("effect/Redacted"));
22
+ var Schema = _interopRequireWildcard(require("effect/Schema"));
21
23
  var _Unify = require("effect/Unify");
22
24
  var HttpApi = _interopRequireWildcard(require("./HttpApi.js"));
23
- var HttpApiEndpoint = _interopRequireWildcard(require("./HttpApiEndpoint.js"));
24
25
  var _HttpApiError = require("./HttpApiError.js");
26
+ var HttpApiMiddleware = _interopRequireWildcard(require("./HttpApiMiddleware.js"));
25
27
  var HttpApiSchema = _interopRequireWildcard(require("./HttpApiSchema.js"));
26
28
  var HttpApp = _interopRequireWildcard(require("./HttpApp.js"));
27
29
  var HttpMethod = _interopRequireWildcard(require("./HttpMethod.js"));
@@ -44,6 +46,17 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
44
46
  * @category router
45
47
  */
46
48
  class Router extends /*#__PURE__*/HttpRouter.Tag("@effect/platform/HttpApiBuilder/Router")() {}
49
+ /**
50
+ * Create a top-level `HttpApi` layer.
51
+ *
52
+ * @since 1.0.0
53
+ * @category constructors
54
+ */
55
+ exports.Router = Router;
56
+ const api = api => Layer.effect(HttpApi.Api, Effect.map(Effect.context(), context => ({
57
+ api: api,
58
+ context
59
+ })));
47
60
  /**
48
61
  * Build an `HttpApp` from an `HttpApi` instance, and serve it using an
49
62
  * `HttpServer`.
@@ -54,8 +67,8 @@ class Router extends /*#__PURE__*/HttpRouter.Tag("@effect/platform/HttpApiBuilde
54
67
  * @since 1.0.0
55
68
  * @category constructors
56
69
  */
57
- exports.Router = Router;
58
- const serve = middleware => httpApp.pipe(Effect.map(HttpServer.serve(middleware)), Layer.unwrapEffect, Layer.provide(Router.Live));
70
+ exports.api = api;
71
+ const serve = middleware => httpApp.pipe(Effect.map(app => HttpServer.serve(app, middleware)), Layer.unwrapEffect, Layer.provide(Router.Live));
59
72
  /**
60
73
  * Construct an `HttpApp` from an `HttpApi` instance.
61
74
  *
@@ -64,12 +77,16 @@ const serve = middleware => httpApp.pipe(Effect.map(HttpServer.serve(middleware)
64
77
  */
65
78
  exports.serve = serve;
66
79
  const httpApp = exports.httpApp = /*#__PURE__*/Effect.gen(function* () {
67
- const api = yield* HttpApi.HttpApi;
68
- const router = yield* Router.router;
80
+ const {
81
+ api,
82
+ context
83
+ } = yield* HttpApi.Api;
84
+ const middleware = makeMiddlewareMap(api.middlewares, context);
85
+ const router = applyMiddleware(middleware, yield* Router.router);
69
86
  const apiMiddleware = yield* Effect.serviceOption(Middleware);
70
87
  const errorSchema = makeErrorSchema(api);
71
88
  const encodeError = Schema.encodeUnknown(errorSchema);
72
- return router.pipe(apiMiddleware._tag === "Some" ? apiMiddleware.value : _Function.identity, Effect.catchAll(error => Effect.matchEffect(encodeError(error), {
89
+ return router.pipe(apiMiddleware._tag === "Some" ? apiMiddleware.value : _Function.identity, Effect.catchAll(error => Effect.matchEffect(Effect.provide(encodeError(error), context), {
73
90
  onFailure: () => Effect.die(error),
74
91
  onSuccess: Effect.succeed
75
92
  })));
@@ -80,51 +97,47 @@ const httpApp = exports.httpApp = /*#__PURE__*/Effect.gen(function* () {
80
97
  * @since 1.0.0
81
98
  * @category constructors
82
99
  * @example
83
- * import { HttpApi } from "@effect/platform"
84
- * import { Etag, HttpApiBuilder, HttpMiddleware, HttpPlatform } from "@effect/platform"
85
- * import { NodeContext } from "@effect/platform-node"
86
- * import { Layer, ManagedRuntime } from "effect"
100
+ * import { HttpApi, HttpApiBuilder, HttpServer } from "@effect/platform"
101
+ * import { Layer } from "effect"
102
+ *
103
+ * class MyApi extends HttpApi.empty {}
87
104
  *
88
- * const ApiLive = HttpApiBuilder.api(HttpApi.empty)
105
+ * const MyApiLive = HttpApiBuilder.api(MyApi)
89
106
  *
90
- * const runtime = ManagedRuntime.make(
107
+ * const { dispose, handler } = HttpApiBuilder.toWebHandler(
91
108
  * Layer.mergeAll(
92
- * ApiLive,
93
- * HttpApiBuilder.Router.Live,
94
- * HttpPlatform.layer,
95
- * Etag.layerWeak
96
- * ).pipe(
97
- * Layer.provideMerge(NodeContext.layer)
109
+ * MyApiLive,
110
+ * // you could also use NodeHttpServer.layerContext, depending on your
111
+ * // server's platform
112
+ * HttpServer.layerContext
98
113
  * )
99
114
  * )
100
- *
101
- * const handler = HttpApiBuilder.toWebHandler(runtime, HttpMiddleware.logger)
102
115
  */
103
- const toWebHandler = (runtime, middleware) => {
116
+ const toWebHandler = (layer, options) => {
117
+ const runtime = ManagedRuntime.make(Layer.merge(layer, Router.Live), options?.memoMap);
118
+ let handlerCached;
104
119
  const handlerPromise = httpApp.pipe(Effect.bindTo("httpApp"), Effect.bind("runtime", () => runtime.runtimeEffect), Effect.map(({
105
120
  httpApp,
106
121
  runtime
107
- }) => HttpApp.toWebHandlerRuntime(runtime)(middleware ? middleware(httpApp) : httpApp)), runtime.runPromise);
108
- return request => handlerPromise.then(handler => handler(request));
122
+ }) => HttpApp.toWebHandlerRuntime(runtime)(options?.middleware ? options.middleware(httpApp) : httpApp)), Effect.tap(handler => {
123
+ handlerCached = handler;
124
+ }), runtime.runPromise);
125
+ function handler(request) {
126
+ if (handlerCached !== undefined) {
127
+ return handlerCached(request);
128
+ }
129
+ return handlerPromise.then(handler => handler(request));
130
+ }
131
+ return {
132
+ handler,
133
+ dispose: runtime.dispose
134
+ };
109
135
  };
110
- /**
111
- * Build a root level `Layer` from an `HttpApi` instance.
112
- *
113
- * The `Layer` will provide the `HttpApi` service, and will require the
114
- * implementation for all the `HttpApiGroup`'s contained in the `HttpApi`.
115
- *
116
- * The resulting `Layer` can be provided to the `HttpApiBuilder.serve` layer.
117
- *
118
- * @since 1.0.0
119
- * @category constructors
120
- */
121
- exports.toWebHandler = toWebHandler;
122
- const api = self => Layer.succeed(HttpApi.HttpApi, self);
123
136
  /**
124
137
  * @since 1.0.0
125
138
  * @category handlers
126
139
  */
127
- exports.api = api;
140
+ exports.toWebHandler = toWebHandler;
128
141
  const HandlersTypeId = exports.HandlersTypeId = /*#__PURE__*/Symbol.for("@effect/platform/HttpApiBuilder/Handlers");
129
142
  const HandlersProto = {
130
143
  [HandlersTypeId]: {
@@ -132,6 +145,28 @@ const HandlersProto = {
132
145
  },
133
146
  pipe() {
134
147
  return (0, _Pipeable.pipeArguments)(this, arguments);
148
+ },
149
+ handle(name, handler) {
150
+ const endpoint = HashMap.unsafeGet(this.group.endpoints, name);
151
+ return makeHandlers({
152
+ group: this.group,
153
+ handlers: Chunk.append(this.handlers, {
154
+ endpoint,
155
+ handler,
156
+ withFullResponse: false
157
+ })
158
+ });
159
+ },
160
+ handleRaw(name, handler) {
161
+ const endpoint = HashMap.unsafeGet(this.group.endpoints, name);
162
+ return makeHandlers({
163
+ group: this.group,
164
+ handlers: Chunk.append(this.handlers, {
165
+ endpoint,
166
+ handler,
167
+ withFullResponse: true
168
+ })
169
+ });
135
170
  }
136
171
  };
137
172
  const makeHandlers = options => {
@@ -141,88 +176,191 @@ const makeHandlers = options => {
141
176
  return self;
142
177
  };
143
178
  /**
144
- * Create a `Layer` that will implement all the endpoints in an `HttpApiGroup`.
179
+ * Create a `Layer` that will implement all the endpoints in an `HttpApi`.
145
180
  *
146
181
  * An unimplemented `Handlers` instance is passed to the `build` function, which
147
182
  * you can use to add handlers to the group.
148
183
  *
149
- * You can implement endpoints using the `HttpApiBuilder.handle` api.
184
+ * You can implement endpoints using the `handlers.handle` api.
150
185
  *
151
186
  * @since 1.0.0
152
187
  * @category handlers
153
188
  */
154
189
  const group = (api, groupName, build) => Router.use(router => Effect.gen(function* () {
155
190
  const context = yield* Effect.context();
156
- const group = Chunk.findFirst(api.groups, group => group.identifier === groupName);
157
- if (group._tag === "None") {
158
- throw new Error(`Group "${groupName}" not found in API`);
159
- }
191
+ const group = HashMap.unsafeGet(api.groups, groupName);
160
192
  const result = build(makeHandlers({
161
- group: group.value,
193
+ group,
162
194
  handlers: Chunk.empty()
163
195
  }));
164
196
  const handlers = Effect.isEffect(result) ? yield* result : result;
197
+ const groupMiddleware = makeMiddlewareMap(group.middlewares, context);
165
198
  const routes = [];
166
199
  for (const item of handlers.handlers) {
167
- if (item._tag === "Middleware") {
168
- for (const route of routes) {
169
- ;
170
- route.handler = item.middleware(route.handler);
171
- }
172
- } else {
173
- routes.push(handlerToRoute(item.endpoint, function (request) {
174
- return Effect.mapInputContext(item.handler(request), input => Context.merge(context, input));
175
- }, item.withFullResponse));
176
- }
200
+ const middleware = makeMiddlewareMap(item.endpoint.middlewares, context, groupMiddleware);
201
+ routes.push(handlerToRoute(item.endpoint, middleware, function (request) {
202
+ return Effect.mapInputContext(item.handler(request), input => Context.merge(context, input));
203
+ }, item.withFullResponse));
177
204
  }
178
205
  yield* router.concat(HttpRouter.fromIterable(routes));
179
206
  }));
180
- /**
181
- * Add the implementation for an `HttpApiEndpoint` to a `Handlers` group.
182
- *
183
- * @since 1.0.0
184
- * @category handlers
185
- */
207
+ // internal
186
208
  exports.group = group;
187
- const handle = (name, handler, options) => self => {
188
- const o = Chunk.findFirst(self.group.endpoints, endpoint => endpoint.name === name);
189
- if (o._tag === "None") {
190
- throw new Error(`Endpoint "${name}" not found in group "${self.group.identifier}"`);
209
+ const requestPayload = (request, urlParams, isMultipart) => HttpMethod.hasBody(request.method) ? isMultipart ? Effect.orDie(request.multipart) : Effect.orDie(request.json) : Effect.succeed(urlParams);
210
+ const makeMiddlewareMap = (middleware, context, initial) => {
211
+ const map = new Map(initial);
212
+ HashSet.forEach(middleware, tag => {
213
+ map.set(tag.key, {
214
+ tag,
215
+ effect: Context.unsafeGet(context, tag)
216
+ });
217
+ });
218
+ return map;
219
+ };
220
+ const handlerToRoute = (endpoint_, middleware, handler, isFullResponse) => {
221
+ const endpoint = endpoint_;
222
+ const decodePath = Option.map(endpoint.pathSchema, Schema.decodeUnknown);
223
+ const isMultipart = endpoint.payloadSchema.pipe(Option.map(schema => HttpApiSchema.getMultipart(schema.ast)), Option.getOrElse(() => false));
224
+ const decodePayload = Option.map(endpoint.payloadSchema, Schema.decodeUnknown);
225
+ const decodeHeaders = Option.map(endpoint.headersSchema, Schema.decodeUnknown);
226
+ const decodeUrlParams = Option.map(endpoint.urlParamsSchema, Schema.decodeUnknown);
227
+ const encodeSuccess = Schema.encode(makeSuccessSchema(endpoint.successSchema));
228
+ return HttpRouter.makeRoute(endpoint.method, endpoint.path, applyMiddleware(middleware, Effect.withFiberRuntime(fiber => {
229
+ const context = fiber.getFiberRef(FiberRef.currentContext);
230
+ const request = Context.unsafeGet(context, HttpServerRequest.HttpServerRequest);
231
+ const routeContext = Context.unsafeGet(context, HttpRouter.RouteContext);
232
+ const urlParams = Context.unsafeGet(context, HttpServerRequest.ParsedSearchParams);
233
+ return (decodePath._tag === "Some" ? decodePath.value(routeContext.params) : Effect.succeed(routeContext.params)).pipe(Effect.bindTo("pathParams"), decodePayload._tag === "Some" ? Effect.bind("payload", _ => Effect.flatMap(requestPayload(request, urlParams, isMultipart), decodePayload.value)) : _Function.identity, decodeHeaders._tag === "Some" ? Effect.bind("headers", _ => decodeHeaders.value(request.headers)) : _Function.identity, decodeUrlParams._tag === "Some" ? Effect.bind("urlParams", _ => decodeUrlParams.value(urlParams)) : _Function.identity, Effect.flatMap(input => {
234
+ const request = {
235
+ path: input.pathParams
236
+ };
237
+ if ("payload" in input) {
238
+ request.payload = input.payload;
239
+ }
240
+ if ("headers" in input) {
241
+ request.headers = input.headers;
242
+ }
243
+ if ("urlParams" in input) {
244
+ request.urlParams = input.urlParams;
245
+ }
246
+ return handler(request);
247
+ }), isFullResponse ? _Function.identity : Effect.flatMap(encodeSuccess), Effect.catchIf(ParseResult.isParseError, _HttpApiError.HttpApiDecodeError.refailParseError));
248
+ })));
249
+ };
250
+ const applyMiddleware = (middleware, handler) => {
251
+ for (const entry of middleware.values()) {
252
+ const effect = HttpApiMiddleware.SecurityTypeId in entry.tag ? makeSecurityMiddleware(entry) : entry.effect;
253
+ if (entry.tag.optional) {
254
+ const previous = handler;
255
+ handler = Effect.matchEffect(effect, {
256
+ onFailure: () => previous,
257
+ onSuccess: entry.tag.provides !== undefined ? value => Effect.provideService(previous, entry.tag.provides, value) : _ => previous
258
+ });
259
+ } else {
260
+ handler = entry.tag.provides !== undefined ? Effect.provideServiceEffect(handler, entry.tag.provides, effect) : Effect.zipRight(effect, handler);
261
+ }
262
+ }
263
+ return handler;
264
+ };
265
+ const securityMiddlewareCache = /*#__PURE__*/(0, _GlobalValue.globalValue)("securityMiddlewareCache", () => new WeakMap());
266
+ const makeSecurityMiddleware = entry => {
267
+ if (securityMiddlewareCache.has(entry.tag)) {
268
+ return securityMiddlewareCache.get(entry.tag);
191
269
  }
192
- const endpoint = o.value;
193
- return makeHandlers({
194
- group: self.group,
195
- handlers: Chunk.append(self.handlers, {
196
- _tag: "Handler",
197
- endpoint,
198
- handler,
199
- withFullResponse: options?.withFullResponse === true
200
- })
270
+ let effect;
271
+ for (const [key, security] of Object.entries(entry.tag.security)) {
272
+ const decode = securityDecode(security);
273
+ const handler = entry.effect[key];
274
+ const middleware = Effect.flatMap(decode, handler);
275
+ effect = effect === undefined ? middleware : Effect.catchAll(effect, () => middleware);
276
+ }
277
+ if (effect === undefined) {
278
+ effect = Effect.void;
279
+ }
280
+ securityMiddlewareCache.set(entry.tag, effect);
281
+ return effect;
282
+ };
283
+ const responseSchema = /*#__PURE__*/Schema.declare(HttpServerResponse.isServerResponse);
284
+ const makeSuccessSchema = schema => {
285
+ const schemas = new Set();
286
+ HttpApiSchema.deunionize(schemas, schema);
287
+ return Schema.Union(...Array.from(schemas, toResponseSuccess));
288
+ };
289
+ const makeErrorSchema = api => {
290
+ const schemas = new Set();
291
+ HttpApiSchema.deunionize(schemas, api.errorSchema);
292
+ HashMap.forEach(api.groups, group => {
293
+ HashMap.forEach(group.endpoints, endpoint => {
294
+ HttpApiSchema.deunionize(schemas, endpoint.errorSchema);
295
+ });
296
+ HttpApiSchema.deunionize(schemas, group.errorSchema);
201
297
  });
298
+ return Schema.Union(...Array.from(schemas, toResponseError));
202
299
  };
203
- /**
204
- * Add `HttpMiddleware` to a `Handlers` group.
205
- *
206
- * Any errors are required to have a corresponding schema in the API.
207
- * You can add middleware errors to an `HttpApiGroup` using the `HttpApiGroup.addError`
208
- * api.
209
- *
210
- * @since 1.0.0
211
- * @category middleware
212
- */
213
- exports.handle = handle;
214
- const middleware = middleware => self => makeHandlers({
215
- ...self,
216
- handlers: Chunk.append(self.handlers, {
217
- _tag: "Middleware",
218
- middleware
219
- })
220
- });
300
+ const decodeForbidden = (_, __, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, _, "Encode only schema"));
301
+ const toResponseSchema = getStatus => {
302
+ const cache = new WeakMap();
303
+ const schemaToResponse = (data, _, ast) => {
304
+ const isEmpty = HttpApiSchema.isVoid(ast.to);
305
+ const status = getStatus(ast.to);
306
+ if (isEmpty) {
307
+ return HttpServerResponse.empty({
308
+ status
309
+ });
310
+ }
311
+ const encoding = HttpApiSchema.getEncoding(ast.to);
312
+ switch (encoding.kind) {
313
+ case "Json":
314
+ {
315
+ return Effect.mapError(HttpServerResponse.json(data, {
316
+ status,
317
+ contentType: encoding.contentType
318
+ }), error => new ParseResult.Type(ast, error, "Could not encode to JSON"));
319
+ }
320
+ case "Text":
321
+ {
322
+ return ParseResult.succeed(HttpServerResponse.text(data, {
323
+ status,
324
+ contentType: encoding.contentType
325
+ }));
326
+ }
327
+ case "Uint8Array":
328
+ {
329
+ return ParseResult.succeed(HttpServerResponse.uint8Array(data, {
330
+ status,
331
+ contentType: encoding.contentType
332
+ }));
333
+ }
334
+ case "UrlParams":
335
+ {
336
+ return ParseResult.succeed(HttpServerResponse.urlParams(data, {
337
+ status,
338
+ contentType: encoding.contentType
339
+ }));
340
+ }
341
+ }
342
+ };
343
+ return schema => {
344
+ if (cache.has(schema.ast)) {
345
+ return cache.get(schema.ast);
346
+ }
347
+ const transform = Schema.transformOrFail(responseSchema, schema, {
348
+ decode: decodeForbidden,
349
+ encode: schemaToResponse
350
+ });
351
+ cache.set(transform.ast, transform);
352
+ return transform;
353
+ };
354
+ };
355
+ const toResponseSuccess = /*#__PURE__*/toResponseSchema(HttpApiSchema.getStatusSuccessAST);
356
+ const toResponseError = /*#__PURE__*/toResponseSchema(HttpApiSchema.getStatusErrorAST);
357
+ // ----------------------------------------------------------------------------
358
+ // Global middleware
359
+ // ----------------------------------------------------------------------------
221
360
  /**
222
361
  * @since 1.0.0
223
362
  * @category middleware
224
363
  */
225
- exports.middleware = middleware;
226
364
  class Middleware extends /*#__PURE__*/Context.Tag("@effect/platform/HttpApiBuilder/Middleware")() {}
227
365
  exports.Middleware = Middleware;
228
366
  const middlewareAdd = middleware => Effect.map(Effect.context(), context => {
@@ -239,7 +377,7 @@ const middlewareAddNoContext = middleware => Effect.map(Effect.serviceOption(Mid
239
377
  * @since 1.0.0
240
378
  * @category middleware
241
379
  */
242
- const middlewareLayer = (...args) => {
380
+ const middleware = (...args) => {
243
381
  const apiFirst = HttpApi.isHttpApi(args[0]);
244
382
  const withContext = apiFirst ? args[2]?.withContext === true : args[1]?.withContext === true;
245
383
  const add = withContext ? middlewareAdd : middlewareAddNoContext;
@@ -253,8 +391,8 @@ const middlewareLayer = (...args) => {
253
391
  * @since 1.0.0
254
392
  * @category middleware
255
393
  */
256
- exports.middlewareLayer = middlewareLayer;
257
- const middlewareLayerScoped = (...args) => {
394
+ exports.middleware = middleware;
395
+ const middlewareScoped = (...args) => {
258
396
  const apiFirst = HttpApi.isHttpApi(args[0]);
259
397
  const withContext = apiFirst ? args[2]?.withContext === true : args[1]?.withContext === true;
260
398
  const add = withContext ? middlewareAdd : middlewareAddNoContext;
@@ -267,8 +405,8 @@ const middlewareLayerScoped = (...args) => {
267
405
  * @since 1.0.0
268
406
  * @category middleware
269
407
  */
270
- exports.middlewareLayerScoped = middlewareLayerScoped;
271
- const middlewareCors = options => middlewareLayer(HttpMiddleware.cors(options));
408
+ exports.middlewareScoped = middlewareScoped;
409
+ const middlewareCors = options => middleware(HttpMiddleware.cors(options));
272
410
  /**
273
411
  * A middleware that adds an openapi.json endpoint to the API.
274
412
  *
@@ -277,7 +415,9 @@ const middlewareCors = options => middlewareLayer(HttpMiddleware.cors(options));
277
415
  */
278
416
  exports.middlewareCors = middlewareCors;
279
417
  const middlewareOpenApi = options => Router.use(router => Effect.gen(function* () {
280
- const api = yield* HttpApi.HttpApi;
418
+ const {
419
+ api
420
+ } = yield* HttpApi.Api;
281
421
  const spec = OpenApi.fromApi(api);
282
422
  const response = yield* HttpServerResponse.json(spec).pipe(Effect.orDie);
283
423
  yield* router.get(options?.path ?? "/openapi.json", Effect.succeed(response));
@@ -286,7 +426,7 @@ exports.middlewareOpenApi = middlewareOpenApi;
286
426
  const bearerLen = `Bearer `.length;
287
427
  /**
288
428
  * @since 1.0.0
289
- * @category middleware
429
+ * @category security
290
430
  */
291
431
  const securityDecode = self => {
292
432
  switch (self._tag) {
@@ -333,9 +473,9 @@ const securityDecode = self => {
333
473
  * You can use this api before returning a response from an endpoint handler.
334
474
  *
335
475
  * ```ts
336
- * ApiBuilder.handle(
476
+ * handlers.handle(
337
477
  * "authenticate",
338
- * (_) => ApiBuilder.securitySetCookie(security, "secret123")
478
+ * (_) => HttpApiBuilder.securitySetCookie(security, "secret123")
339
479
  * )
340
480
  * ```
341
481
  *
@@ -351,155 +491,5 @@ const securitySetCookie = (self, value, options) => {
351
491
  ...options
352
492
  })));
353
493
  };
354
- /**
355
- * Make a middleware from an `HttpApiSecurity` instance, that can be used when
356
- * constructing a `Handlers` group.
357
- *
358
- * @since 1.0.0
359
- * @category middleware
360
- * @example
361
- * import { HttpApiBuilder, HttpApiSecurity } from "@effect/platform"
362
- * import { Schema } from "@effect/schema"
363
- * import { Context, Effect, Redacted } from "effect"
364
- *
365
- * class User extends Schema.Class<User>("User")({
366
- * id: Schema.Number
367
- * }) {}
368
- *
369
- * class CurrentUser extends Context.Tag("CurrentUser")<CurrentUser, User>() {}
370
- *
371
- * class Accounts extends Context.Tag("Accounts")<Accounts, {
372
- * readonly findUserByAccessToken: (accessToken: string) => Effect.Effect<User>
373
- * }>() {}
374
- *
375
- * const securityMiddleware = Effect.gen(function*() {
376
- * const accounts = yield* Accounts
377
- * return HttpApiBuilder.middlewareSecurity(
378
- * HttpApiSecurity.bearer,
379
- * CurrentUser,
380
- * (token) => accounts.findUserByAccessToken(Redacted.value(token))
381
- * )
382
- * })
383
- */
384
494
  exports.securitySetCookie = securitySetCookie;
385
- const middlewareSecurity = (self, tag, f) => middleware(Effect.provideServiceEffect(tag, Effect.flatMap(securityDecode(self), f)));
386
- /**
387
- * Make a middleware from an `HttpApiSecurity` instance, that can be used when
388
- * constructing a `Handlers` group.
389
- *
390
- * This version does not supply any context to the handlers.
391
- *
392
- * @since 1.0.0
393
- * @category middleware
394
- */
395
- exports.middlewareSecurity = middlewareSecurity;
396
- const middlewareSecurityVoid = (self, f) => middleware(httpApp => securityDecode(self).pipe(Effect.flatMap(f), Effect.zipRight(httpApp)));
397
- // internal
398
- exports.middlewareSecurityVoid = middlewareSecurityVoid;
399
- const requestPayload = (request, urlParams, isMultipart) => HttpMethod.hasBody(request.method) ? isMultipart ? Effect.orDie(request.multipart) : Effect.orDie(request.json) : Effect.succeed(urlParams);
400
- const handlerToRoute = (endpoint, handler, isFullResponse) => {
401
- const decodePath = Option.map(endpoint.pathSchema, Schema.decodeUnknown);
402
- const isMultipart = endpoint.payloadSchema.pipe(Option.map(schema => HttpApiSchema.getMultipart(schema.ast)), Option.getOrElse(() => false));
403
- const decodePayload = Option.map(endpoint.payloadSchema, Schema.decodeUnknown);
404
- const decodeHeaders = Option.map(endpoint.headersSchema, Schema.decodeUnknown);
405
- const encoding = HttpApiSchema.getEncoding(endpoint.successSchema.ast);
406
- const successStatus = HttpApiSchema.getStatusSuccess(endpoint.successSchema);
407
- const encodeSuccess = Option.map(HttpApiEndpoint.schemaSuccess(endpoint), schema => {
408
- const encode = Schema.encodeUnknown(schema);
409
- switch (encoding.kind) {
410
- case "Json":
411
- {
412
- return body => Effect.orDie(Effect.flatMap(encode(body), json => HttpServerResponse.json(json, {
413
- status: successStatus,
414
- contentType: encoding.contentType
415
- })));
416
- }
417
- case "Text":
418
- {
419
- return body => Effect.map(Effect.orDie(encode(body)), text => HttpServerResponse.text(text, {
420
- status: successStatus,
421
- contentType: encoding.contentType
422
- }));
423
- }
424
- case "Uint8Array":
425
- {
426
- return body => Effect.map(Effect.orDie(encode(body)), data => HttpServerResponse.uint8Array(data, {
427
- status: successStatus,
428
- contentType: encoding.contentType
429
- }));
430
- }
431
- case "UrlParams":
432
- {
433
- return body => Effect.map(Effect.orDie(encode(body)), params => HttpServerResponse.urlParams(params, {
434
- status: successStatus,
435
- contentType: encoding.contentType
436
- }));
437
- }
438
- }
439
- });
440
- return HttpRouter.makeRoute(endpoint.method, endpoint.path, Effect.withFiberRuntime(fiber => {
441
- const context = fiber.getFiberRef(FiberRef.currentContext);
442
- const request = Context.unsafeGet(context, HttpServerRequest.HttpServerRequest);
443
- const routeContext = Context.unsafeGet(context, HttpRouter.RouteContext);
444
- const urlParams = Context.unsafeGet(context, HttpServerRequest.ParsedSearchParams);
445
- return (decodePath._tag === "Some" ? Effect.catchAll(decodePath.value(routeContext.params), _HttpApiError.HttpApiDecodeError.refailParseError) : Effect.succeed(routeContext.params)).pipe(Effect.bindTo("pathParams"), decodePayload._tag === "Some" ? Effect.bind("payload", _ => requestPayload(request, urlParams, isMultipart).pipe(Effect.orDie, Effect.flatMap(raw => Effect.catchAll(decodePayload.value(raw), _HttpApiError.HttpApiDecodeError.refailParseError)))) : _Function.identity, decodeHeaders._tag === "Some" ? Effect.bind("headers", _ => Effect.orDie(decodeHeaders.value(request.headers))) : _Function.identity, Effect.flatMap(input => {
446
- const request = {
447
- path: input.pathParams
448
- };
449
- if ("payload" in input) {
450
- request.payload = input.payload;
451
- }
452
- if ("headers" in input) {
453
- request.headers = input.headers;
454
- }
455
- return handler(request);
456
- }), isFullResponse ? _Function.identity : encodeSuccess._tag === "Some" ? Effect.flatMap(encodeSuccess.value) : Effect.as(HttpServerResponse.empty({
457
- status: successStatus
458
- })));
459
- }));
460
- };
461
- const astCache = /*#__PURE__*/(0, _GlobalValue.globalValue)("@effect/platform/HttpApiBuilder/astCache", () => new WeakMap());
462
- const makeErrorSchema = api => {
463
- const schemas = new Set();
464
- function processSchema(schema) {
465
- if (astCache.has(schema.ast)) {
466
- schemas.add(astCache.get(schema.ast));
467
- return;
468
- }
469
- const ast = schema.ast;
470
- if (ast._tag === "Union") {
471
- for (const astType of ast.types) {
472
- const errorSchema = Schema.make(astType).annotations({
473
- ...ast.annotations,
474
- ...astType.annotations
475
- });
476
- astCache.set(astType, errorSchema);
477
- schemas.add(errorSchema);
478
- }
479
- } else {
480
- astCache.set(ast, schema);
481
- schemas.add(schema);
482
- }
483
- }
484
- processSchema(api.errorSchema);
485
- for (const group of api.groups) {
486
- for (const endpoint of group.endpoints) {
487
- processSchema(endpoint.errorSchema);
488
- }
489
- processSchema(group.errorSchema);
490
- }
491
- return Schema.Union(...[...schemas].map(schema => {
492
- const status = HttpApiSchema.getStatusError(schema);
493
- const encoded = AST.encodedAST(schema.ast);
494
- const isEmpty = encoded._tag === "VoidKeyword";
495
- return Schema.transformOrFail(Schema.Any, schema, {
496
- decode: (_, __, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, _, "Encode only schema")),
497
- encode: (error, _, ast) => isEmpty ? HttpServerResponse.empty({
498
- status
499
- }) : HttpServerResponse.json(error, {
500
- status
501
- }).pipe(Effect.mapError(error => new ParseResult.Type(ast, error, "Could not encode to JSON")))
502
- });
503
- }));
504
- };
505
495
  //# sourceMappingURL=HttpApiBuilder.js.map