@forklaunch/hyper-express 0.4.11 → 0.5.2

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.
package/lib/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
1
  import * as _forklaunch_hyper_express_fork from '@forklaunch/hyper-express-fork';
2
- import { Server, MiddlewareHandler, Request, Response, MiddlewareNext, Router as Router$1 } from '@forklaunch/hyper-express-fork';
2
+ import { Server, MiddlewareHandler, Request, Response, MiddlewareNext, ServerConstructorOptions, Router as Router$1 } from '@forklaunch/hyper-express-fork';
3
3
  export { MiddlewareNext as NextFunction, Request, Response } from '@forklaunch/hyper-express-fork';
4
4
  import * as _forklaunch_core_http from '@forklaunch/core/http';
5
5
  import { ForklaunchExpressLikeApplication, OpenTelemetryCollector, MetricsDefinition, DocsConfiguration, ForklaunchExpressLikeRouter, ForklaunchRouter, TypedMiddlewareDefinition } from '@forklaunch/core/http';
6
6
  import { AnySchemaValidator } from '@forklaunch/validator';
7
+ import { BusboyConfig } from 'busboy';
7
8
  import * as uWebsockets from 'uWebSockets.js';
8
9
  export { ParsedQs } from 'qs';
9
10
 
@@ -30,7 +31,7 @@ export { ParsedQs } from 'qs';
30
31
  * ```
31
32
  */
32
33
  declare class Application<SV extends AnySchemaValidator> extends ForklaunchExpressLikeApplication<SV, Server, MiddlewareHandler, Request<Record<string, unknown>>, Response<Record<string, unknown>>, MiddlewareNext> {
33
- private readonly docsConfiguration?;
34
+ private readonly configurationOptions?;
34
35
  /**
35
36
  * Creates an instance of the Application class.
36
37
  *
@@ -47,7 +48,11 @@ declare class Application<SV extends AnySchemaValidator> extends ForklaunchExpre
47
48
  * );
48
49
  * ```
49
50
  */
50
- constructor(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, docsConfiguration?: DocsConfiguration | undefined);
51
+ constructor(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, configurationOptions?: {
52
+ docs?: DocsConfiguration;
53
+ busboy?: BusboyConfig;
54
+ server?: ServerConstructorOptions;
55
+ } | undefined);
51
56
  /**
52
57
  * Starts the server and sets up API documentation.
53
58
  * Supports multiple listening configurations including port, host, and UNIX socket path.
@@ -82,7 +87,9 @@ declare class Application<SV extends AnySchemaValidator> extends ForklaunchExpre
82
87
 
83
88
  declare class Router<SV extends AnySchemaValidator, BasePath extends `/${string}`> extends ForklaunchExpressLikeRouter<SV, BasePath, MiddlewareHandler, Router$1, Request<Record<string, unknown>>, Response<Record<string, unknown>>, MiddlewareNext> implements ForklaunchRouter<SV> {
84
89
  basePath: BasePath;
85
- constructor(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>);
90
+ constructor(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options?: {
91
+ busboy?: BusboyConfig;
92
+ });
86
93
  route(path: string): this;
87
94
  any: TypedMiddlewareDefinition<this, SV, Request<Record<string, unknown>>, Response<Record<string, unknown>>, MiddlewareNext>;
88
95
  }
@@ -95,7 +102,11 @@ type App<SV extends AnySchemaValidator> = Application<SV>;
95
102
  * @param {SV} schemaValidator - The schema validator.
96
103
  * @returns {Application<SV>} - The new application instance.
97
104
  */
98
- declare function forklaunchExpress<SV extends AnySchemaValidator>(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, docsConfiguration?: DocsConfiguration): Application<SV>;
105
+ declare function forklaunchExpress<SV extends AnySchemaValidator>(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options?: {
106
+ docs?: DocsConfiguration;
107
+ busboy?: BusboyConfig;
108
+ server?: ServerConstructorOptions;
109
+ }): Application<SV>;
99
110
  /**
100
111
  * Creates a new instance of Router with the given base path and schema validator.
101
112
  *
@@ -104,7 +115,9 @@ declare function forklaunchExpress<SV extends AnySchemaValidator>(schemaValidato
104
115
  * @param {SV} schemaValidator - The schema validator.
105
116
  * @returns {Router<SV>} - The new router instance.
106
117
  */
107
- declare function forklaunchRouter<SV extends AnySchemaValidator, BasePath extends `/${string}`>(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>): Router<SV, BasePath>;
118
+ declare function forklaunchRouter<SV extends AnySchemaValidator, BasePath extends `/${string}`>(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options?: {
119
+ busboy?: BusboyConfig;
120
+ }): Router<SV, BasePath>;
108
121
 
109
122
  declare const handlers: {
110
123
  any: <SV extends AnySchemaValidator, Path extends `/${string}`, P extends _forklaunch_core_http.ParamsObject<SV>, ResBodyMap extends _forklaunch_core_http.ResponsesObject<SV>, ReqBody extends _forklaunch_core_http.Body<SV>, ReqQuery extends _forklaunch_core_http.QueryObject<SV>, ReqHeaders extends _forklaunch_core_http.HeadersObject<SV>, ResHeaders extends _forklaunch_core_http.HeadersObject<SV>, LocalsObj extends Record<string, unknown>>(schemaValidator: SV, path: Path, contractDetails: _forklaunch_core_http.ContractDetails<SV, "middleware", Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, _forklaunch_hyper_express_fork.Request<LocalsObj>>, ...handlers: _forklaunch_core_http.ExpressLikeSchemaHandler<SV, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, _forklaunch_hyper_express_fork.Request<LocalsObj>, _forklaunch_hyper_express_fork.Response<LocalsObj>, _forklaunch_hyper_express_fork.MiddlewareNext>[]) => {
package/lib/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import * as _forklaunch_hyper_express_fork from '@forklaunch/hyper-express-fork';
2
- import { Server, MiddlewareHandler, Request, Response, MiddlewareNext, Router as Router$1 } from '@forklaunch/hyper-express-fork';
2
+ import { Server, MiddlewareHandler, Request, Response, MiddlewareNext, ServerConstructorOptions, Router as Router$1 } from '@forklaunch/hyper-express-fork';
3
3
  export { MiddlewareNext as NextFunction, Request, Response } from '@forklaunch/hyper-express-fork';
4
4
  import * as _forklaunch_core_http from '@forklaunch/core/http';
5
5
  import { ForklaunchExpressLikeApplication, OpenTelemetryCollector, MetricsDefinition, DocsConfiguration, ForklaunchExpressLikeRouter, ForklaunchRouter, TypedMiddlewareDefinition } from '@forklaunch/core/http';
6
6
  import { AnySchemaValidator } from '@forklaunch/validator';
7
+ import { BusboyConfig } from 'busboy';
7
8
  import * as uWebsockets from 'uWebSockets.js';
8
9
  export { ParsedQs } from 'qs';
9
10
 
@@ -30,7 +31,7 @@ export { ParsedQs } from 'qs';
30
31
  * ```
31
32
  */
32
33
  declare class Application<SV extends AnySchemaValidator> extends ForklaunchExpressLikeApplication<SV, Server, MiddlewareHandler, Request<Record<string, unknown>>, Response<Record<string, unknown>>, MiddlewareNext> {
33
- private readonly docsConfiguration?;
34
+ private readonly configurationOptions?;
34
35
  /**
35
36
  * Creates an instance of the Application class.
36
37
  *
@@ -47,7 +48,11 @@ declare class Application<SV extends AnySchemaValidator> extends ForklaunchExpre
47
48
  * );
48
49
  * ```
49
50
  */
50
- constructor(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, docsConfiguration?: DocsConfiguration | undefined);
51
+ constructor(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, configurationOptions?: {
52
+ docs?: DocsConfiguration;
53
+ busboy?: BusboyConfig;
54
+ server?: ServerConstructorOptions;
55
+ } | undefined);
51
56
  /**
52
57
  * Starts the server and sets up API documentation.
53
58
  * Supports multiple listening configurations including port, host, and UNIX socket path.
@@ -82,7 +87,9 @@ declare class Application<SV extends AnySchemaValidator> extends ForklaunchExpre
82
87
 
83
88
  declare class Router<SV extends AnySchemaValidator, BasePath extends `/${string}`> extends ForklaunchExpressLikeRouter<SV, BasePath, MiddlewareHandler, Router$1, Request<Record<string, unknown>>, Response<Record<string, unknown>>, MiddlewareNext> implements ForklaunchRouter<SV> {
84
89
  basePath: BasePath;
85
- constructor(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>);
90
+ constructor(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options?: {
91
+ busboy?: BusboyConfig;
92
+ });
86
93
  route(path: string): this;
87
94
  any: TypedMiddlewareDefinition<this, SV, Request<Record<string, unknown>>, Response<Record<string, unknown>>, MiddlewareNext>;
88
95
  }
@@ -95,7 +102,11 @@ type App<SV extends AnySchemaValidator> = Application<SV>;
95
102
  * @param {SV} schemaValidator - The schema validator.
96
103
  * @returns {Application<SV>} - The new application instance.
97
104
  */
98
- declare function forklaunchExpress<SV extends AnySchemaValidator>(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, docsConfiguration?: DocsConfiguration): Application<SV>;
105
+ declare function forklaunchExpress<SV extends AnySchemaValidator>(schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options?: {
106
+ docs?: DocsConfiguration;
107
+ busboy?: BusboyConfig;
108
+ server?: ServerConstructorOptions;
109
+ }): Application<SV>;
99
110
  /**
100
111
  * Creates a new instance of Router with the given base path and schema validator.
101
112
  *
@@ -104,7 +115,9 @@ declare function forklaunchExpress<SV extends AnySchemaValidator>(schemaValidato
104
115
  * @param {SV} schemaValidator - The schema validator.
105
116
  * @returns {Router<SV>} - The new router instance.
106
117
  */
107
- declare function forklaunchRouter<SV extends AnySchemaValidator, BasePath extends `/${string}`>(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>): Router<SV, BasePath>;
118
+ declare function forklaunchRouter<SV extends AnySchemaValidator, BasePath extends `/${string}`>(basePath: BasePath, schemaValidator: SV, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options?: {
119
+ busboy?: BusboyConfig;
120
+ }): Router<SV, BasePath>;
108
121
 
109
122
  declare const handlers: {
110
123
  any: <SV extends AnySchemaValidator, Path extends `/${string}`, P extends _forklaunch_core_http.ParamsObject<SV>, ResBodyMap extends _forklaunch_core_http.ResponsesObject<SV>, ReqBody extends _forklaunch_core_http.Body<SV>, ReqQuery extends _forklaunch_core_http.QueryObject<SV>, ReqHeaders extends _forklaunch_core_http.HeadersObject<SV>, ResHeaders extends _forklaunch_core_http.HeadersObject<SV>, LocalsObj extends Record<string, unknown>>(schemaValidator: SV, path: Path, contractDetails: _forklaunch_core_http.ContractDetails<SV, "middleware", Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, _forklaunch_hyper_express_fork.Request<LocalsObj>>, ...handlers: _forklaunch_core_http.ExpressLikeSchemaHandler<SV, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, _forklaunch_hyper_express_fork.Request<LocalsObj>, _forklaunch_hyper_express_fork.Response<LocalsObj>, _forklaunch_hyper_express_fork.MiddlewareNext>[]) => {
package/lib/index.js CHANGED
@@ -97,9 +97,121 @@ var trace = (schemaValidator, path, contractDetails, ...handlers2) => {
97
97
  };
98
98
 
99
99
  // src/hyperExpressApplication.ts
100
- var import_http11 = require("@forklaunch/core/http");
100
+ var import_common3 = require("@forklaunch/common");
101
+ var import_http13 = require("@forklaunch/core/http");
101
102
  var import_hyper_express_fork = require("@forklaunch/hyper-express-fork");
102
103
  var import_express_api_reference = require("@scalar/express-api-reference");
104
+ var import_crypto = __toESM(require("crypto"));
105
+
106
+ // src/middleware/contentParse.middleware.ts
107
+ var import_common = require("@forklaunch/common");
108
+ var import_http11 = require("@forklaunch/core/http");
109
+ function contentParse(options2) {
110
+ return async (req) => {
111
+ const coercedRequest = req;
112
+ const discriminatedBody = (0, import_http11.discriminateBody)(
113
+ coercedRequest.schemaValidator,
114
+ coercedRequest.contractDetails.body
115
+ );
116
+ if (discriminatedBody != null) {
117
+ switch (discriminatedBody.parserType) {
118
+ case "file":
119
+ req.body = await req.buffer();
120
+ break;
121
+ case "json":
122
+ req.body = await req.json();
123
+ break;
124
+ case "multipart": {
125
+ const body = {};
126
+ await req.multipart(options2?.busboy ?? {}, async (field) => {
127
+ if (field.file) {
128
+ let buffer = "";
129
+ for await (const chunk of field.file.stream) {
130
+ buffer += chunk;
131
+ }
132
+ const fileBuffer = Buffer.from(buffer);
133
+ body[field.name] = fileBuffer.toString();
134
+ } else {
135
+ body[field.name] = field.value;
136
+ }
137
+ });
138
+ req.body = body;
139
+ break;
140
+ }
141
+ case "text":
142
+ req.body = await req.text();
143
+ break;
144
+ case "urlEncoded":
145
+ req.body = await req.urlencoded();
146
+ break;
147
+ default:
148
+ (0, import_common.isNever)(discriminatedBody.parserType);
149
+ }
150
+ }
151
+ };
152
+ }
153
+
154
+ // src/middleware/enrichResponseTransmission.middleware.ts
155
+ var import_common2 = require("@forklaunch/common");
156
+ var import_http12 = require("@forklaunch/core/http");
157
+ function enrichResponseTransmission(req, res, next) {
158
+ const originalSend = res.send;
159
+ const originalJson = res.json;
160
+ const originalSetHeader = res.setHeader;
161
+ res.json = function(data) {
162
+ res.statusCode = Number(res._status_code);
163
+ (0, import_http12.enrichExpressLikeSend)(
164
+ this,
165
+ req,
166
+ res,
167
+ originalJson,
168
+ originalSend,
169
+ data,
170
+ !res.cors && (res._cork && !res._corked || !res._cork)
171
+ );
172
+ return data;
173
+ };
174
+ res.send = function(data) {
175
+ res.statusCode = Number(res._status_code);
176
+ if (res.sent) {
177
+ originalSend.call(this, data);
178
+ return true;
179
+ }
180
+ (0, import_http12.enrichExpressLikeSend)(
181
+ this,
182
+ req,
183
+ res,
184
+ originalSend,
185
+ originalSend,
186
+ data,
187
+ !res.cors && (res._cork && !res._corked || !res._cork)
188
+ );
189
+ res.sent = true;
190
+ return true;
191
+ };
192
+ res.setHeader = function(name, value) {
193
+ let stringifiedValue;
194
+ if (Array.isArray(value)) {
195
+ stringifiedValue = value.map((v) => (0, import_common2.safeStringify)(v)).join("\n");
196
+ } else {
197
+ stringifiedValue = (0, import_common2.safeStringify)(value);
198
+ }
199
+ return originalSetHeader.call(this, name, stringifiedValue);
200
+ };
201
+ res.sseEmitter = function(emitter) {
202
+ const generator = emitter();
203
+ (0, import_http12.enrichExpressLikeSend)(
204
+ this,
205
+ req,
206
+ res,
207
+ originalSend,
208
+ originalSend,
209
+ generator,
210
+ !res.cors && (res._cork && !res._corked || !res._cork)
211
+ );
212
+ };
213
+ next();
214
+ }
103
215
 
104
216
  // src/middleware/swagger.middleware.ts
105
217
  var import_live_directory = __toESM(require("live-directory"));
@@ -160,7 +272,7 @@ function swagger(path, document, opts, options2, customCss, customfavIcon, swagg
160
272
  }
161
273
 
162
274
  // src/hyperExpressApplication.ts
163
- var Application = class extends import_http11.ForklaunchExpressLikeApplication {
275
+ var Application = class extends import_http13.ForklaunchExpressLikeApplication {
164
276
  /**
165
277
  * Creates an instance of the Application class.
166
278
  *
@@ -177,54 +289,66 @@ var Application = class extends import_http11.ForklaunchExpressLikeApplication {
177
289
  * );
178
290
  * ```
179
291
  */
180
- constructor(schemaValidator, openTelemetryCollector, docsConfiguration) {
181
- super(schemaValidator, new import_hyper_express_fork.Server(), openTelemetryCollector);
182
- this.docsConfiguration = docsConfiguration;
292
+ constructor(schemaValidator, openTelemetryCollector, configurationOptions) {
293
+ super(
294
+ schemaValidator,
295
+ new import_hyper_express_fork.Server(configurationOptions?.server),
296
+ [
297
+ contentParse(configurationOptions),
298
+ enrichResponseTransmission
299
+ ],
300
+ openTelemetryCollector
301
+ );
302
+ this.configurationOptions = configurationOptions;
183
303
  }
184
304
  async listen(arg0, arg1, arg2) {
185
305
  if (typeof arg0 === "number") {
186
306
  const port = arg0 || Number(process.env.PORT);
187
307
  this.internal.set_error_handler((req, res, err) => {
308
+ const statusCode = Number(res.statusCode);
188
309
  res.locals.errorMessage = err.message;
189
310
  res.type("text/plain");
190
- res.status(
191
- res.statusCode && res.statusCode >= 400 ? res.statusCode : 500
192
- ).send(
311
+ res.status(statusCode && statusCode >= 400 ? statusCode : 500).send(
193
312
  `Internal server error:
194
313
 
195
- Correlation id: ${(0, import_http11.isForklaunchRequest)(req) ? req.context.correlationId : "No correlation ID"}`
314
+ Correlation id: ${(0, import_http13.isForklaunchRequest)(req) ? req.context.correlationId : "No correlation ID"}`
196
315
  );
197
- (0, import_http11.logger)("error").error(err.stack ?? err.message, {
198
- [import_http11.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode ?? 500
316
+ (0, import_http13.logger)("error").error(err.stack ?? err.message, {
317
+ [import_http13.ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode ?? 500
199
318
  });
200
319
  });
201
- if (this.docsConfiguration == null || this.docsConfiguration.type === "scalar") {
320
+ const openApi = (0, import_http13.generateSwaggerDocument)(
321
+ this.schemaValidator,
322
+ port,
323
+ this.routers
324
+ );
325
+ if (this.configurationOptions?.docs == null || this.configurationOptions?.docs.type === "scalar") {
202
326
  this.internal.use(
203
327
  `/api/${process.env.VERSION ?? "v1"}${process.env.DOCS_PATH ?? "/docs"}`,
204
328
  (0, import_express_api_reference.apiReference)({
205
- content: (0, import_http11.generateSwaggerDocument)(
206
- this.schemaValidator,
207
- port,
208
- this.routers
209
- ),
210
- ...this.docsConfiguration
329
+ content: openApi,
330
+ ...this.configurationOptions?.docs
211
331
  })
212
332
  );
213
- } else if (this.docsConfiguration.type === "swagger") {
333
+ } else if (this.configurationOptions?.docs.type === "swagger") {
214
334
  const swaggerPath = `/api/${process.env.VERSION ?? "v1"}${process.env.DOCS_PATH ?? "/docs"}`;
215
335
  this.internal.use(swaggerPath, swaggerRedirect(swaggerPath));
216
- this.internal.get(
217
- `${swaggerPath}/*`,
218
- swagger(
219
- swaggerPath,
220
- (0, import_http11.generateSwaggerDocument)(
221
- this.schemaValidator,
222
- port,
223
- this.routers
224
- )
225
- )
226
- );
336
+ this.internal.get(`${swaggerPath}/*`, swagger(swaggerPath, openApi));
227
337
  }
338
+ this.internal.get(
339
+ `/api/${process.env.VERSION ?? "v1"}/openapi`,
340
+ (_, res) => {
341
+ res.type("application/json");
342
+ res.json(openApi);
343
+ }
344
+ );
345
+ this.internal.get(
346
+ `/api/${process.env.VERSION ?? "v1"}/openapi-hash`,
347
+ async (_, res) => {
348
+ const hash = await import_crypto.default.createHash("sha256").update((0, import_common3.safeStringify)(openApi)).digest("hex");
349
+ res.send(hash);
350
+ }
351
+ );
228
352
  if (arg1 && typeof arg1 === "string") {
229
353
  return this.internal.listen(port, arg1, arg2);
230
354
  } else if (arg1 && typeof arg1 === "function") {
@@ -239,95 +363,9 @@ Correlation id: ${(0, import_http11.isForklaunchRequest)(req) ? req.context.corr
239
363
  };
240
364
 
241
365
  // src/hyperExpressRouter.ts
242
- var import_http13 = require("@forklaunch/core/http");
366
+ var import_http14 = require("@forklaunch/core/http");
243
367
  var import_hyper_express_fork2 = require("@forklaunch/hyper-express-fork");
244
368
 
245
- // src/middleware/contentParse.middleware.ts
246
- async function contentParse(req) {
247
- switch (req.headers["content-type"] && req.headers["content-type"].split(";")[0]) {
248
- case "application/json":
249
- req.body = await req.json();
250
- break;
251
- case "application/x-www-form-urlencoded":
252
- req.body = await req.urlencoded();
253
- break;
254
- case "text/plain":
255
- req.body = await req.text();
256
- break;
257
- case "application/octet-stream":
258
- req.body = await req.buffer();
259
- break;
260
- case "multipart/form-data":
261
- req.body = {};
262
- await req.multipart(async (field) => {
263
- if (field.file) {
264
- const fileBuffer = Buffer.from(
265
- await field.file.stream.read(),
266
- field.encoding
267
- );
268
- req.body[field.name] = {
269
- buffer: fileBuffer,
270
- name: field.file.name ?? field.name,
271
- type: field.mime_type
272
- };
273
- } else {
274
- req.body[field.name] = field.value;
275
- }
276
- });
277
- break;
278
- default:
279
- req.body = await req.json();
280
- break;
281
- }
282
- }
283
-
284
- // src/middleware/enrichResponseTransmission.middleware.ts
285
- var import_http12 = require("@forklaunch/core/http");
286
- function enrichResponseTransmission(req, res, next) {
287
- const originalSend = res.send;
288
- const originalJson = res.json;
289
- const originalSetHeader = res.setHeader;
290
- res.json = function(data) {
291
- res.bodyData = data;
292
- res.statusCode = res._status_code;
293
- (0, import_http12.enrichExpressLikeSend)(
294
- this,
295
- req,
296
- res,
297
- originalJson,
298
- data,
299
- !res.cors && (res._cork && !res._corked || !res._cork)
300
- );
301
- return data;
302
- };
303
- res.send = function(data) {
304
- if (!res.bodyData) {
305
- res.bodyData = data;
306
- res.statusCode = res._status_code;
307
- }
308
- return (0, import_http12.enrichExpressLikeSend)(
309
- this,
310
- req,
311
- res,
312
- originalSend,
313
- data,
314
- !res.cors && (res._cork && !res._corked || !res._cork)
315
- );
316
- };
317
- res.setHeader = function(name, value) {
318
- let stringifiedValue;
319
- if (Array.isArray(value)) {
320
- stringifiedValue = value.map(
321
- (v) => typeof v !== "string" ? JSON.stringify(v) : v
322
- );
323
- } else {
324
- stringifiedValue = typeof value !== "string" ? JSON.stringify(value) : value;
325
- }
326
- return originalSetHeader.call(this, name, stringifiedValue);
327
- };
328
- next();
329
- }
330
-
331
369
  // src/middleware/polyfillGetHeaders.middleware.ts
332
370
  function polyfillGetHeaders(_req, res, next) {
333
371
  res.getHeaders = () => {
@@ -337,20 +375,20 @@ function polyfillGetHeaders(_req, res, next) {
337
375
  }
338
376
 
339
377
  // src/hyperExpressRouter.ts
340
- var Router = class extends import_http13.ForklaunchExpressLikeRouter {
341
- constructor(basePath, schemaValidator, openTelemetryCollector) {
378
+ var Router = class extends import_http14.ForklaunchExpressLikeRouter {
379
+ constructor(basePath, schemaValidator, openTelemetryCollector, options2) {
342
380
  super(
343
381
  basePath,
344
382
  schemaValidator,
345
383
  new import_hyper_express_fork2.Router(),
384
+ [
385
+ contentParse(options2),
386
+ enrichResponseTransmission
387
+ ],
346
388
  openTelemetryCollector
347
389
  );
348
390
  this.basePath = basePath;
349
391
  this.internal.use(polyfillGetHeaders);
350
- this.internal.use(contentParse);
351
- this.internal.use(
352
- enrichResponseTransmission
353
- );
354
392
  }
355
393
  route(path) {
356
394
  this.internal.route(path);
@@ -370,15 +408,16 @@ var Router = class extends import_http13.ForklaunchExpressLikeRouter {
370
408
  };
371
409
 
372
410
  // index.ts
373
- function forklaunchExpress(schemaValidator, openTelemetryCollector, docsConfiguration) {
374
- return new Application(
411
+ function forklaunchExpress(schemaValidator, openTelemetryCollector, options2) {
412
+ return new Application(schemaValidator, openTelemetryCollector, options2);
413
+ }
414
+ function forklaunchRouter(basePath, schemaValidator, openTelemetryCollector, options2) {
415
+ const router = new Router(
416
+ basePath,
375
417
  schemaValidator,
376
418
  openTelemetryCollector,
377
- docsConfiguration
419
+ options2
378
420
  );
379
- }
380
- function forklaunchRouter(basePath, schemaValidator, openTelemetryCollector) {
381
- const router = new Router(basePath, schemaValidator, openTelemetryCollector);
382
421
  return router;
383
422
  }
384
423
  var handlers = {
package/lib/index.mjs CHANGED
@@ -79,6 +79,7 @@ var trace = (schemaValidator, path, contractDetails, ...handlers2) => {
79
79
  };
80
80
 
81
81
  // src/hyperExpressApplication.ts
82
+ import { safeStringify as safeStringify2 } from "@forklaunch/common";
82
83
  import {
83
84
  ATTR_HTTP_RESPONSE_STATUS_CODE,
84
85
  ForklaunchExpressLikeApplication,
@@ -90,6 +91,119 @@ import {
90
91
  Server
91
92
  } from "@forklaunch/hyper-express-fork";
92
93
  import { apiReference } from "@scalar/express-api-reference";
94
+ import crypto from "crypto";
95
+
96
+ // src/middleware/contentParse.middleware.ts
97
+ import { isNever } from "@forklaunch/common";
98
+ import { discriminateBody } from "@forklaunch/core/http";
99
+ function contentParse(options2) {
100
+ return async (req) => {
101
+ const coercedRequest = req;
102
+ const discriminatedBody = discriminateBody(
103
+ coercedRequest.schemaValidator,
104
+ coercedRequest.contractDetails.body
105
+ );
106
+ if (discriminatedBody != null) {
107
+ switch (discriminatedBody.parserType) {
108
+ case "file":
109
+ req.body = await req.buffer();
110
+ break;
111
+ case "json":
112
+ req.body = await req.json();
113
+ break;
114
+ case "multipart": {
115
+ const body = {};
116
+ await req.multipart(options2?.busboy ?? {}, async (field) => {
117
+ if (field.file) {
118
+ let buffer = "";
119
+ for await (const chunk of field.file.stream) {
120
+ buffer += chunk;
121
+ }
122
+ const fileBuffer = Buffer.from(buffer);
123
+ body[field.name] = fileBuffer.toString();
124
+ } else {
125
+ body[field.name] = field.value;
126
+ }
127
+ });
128
+ req.body = body;
129
+ break;
130
+ }
131
+ case "text":
132
+ req.body = await req.text();
133
+ break;
134
+ case "urlEncoded":
135
+ req.body = await req.urlencoded();
136
+ break;
137
+ default:
138
+ isNever(discriminatedBody.parserType);
139
+ }
140
+ }
141
+ };
142
+ }
143
+
144
+ // src/middleware/enrichResponseTransmission.middleware.ts
145
+ import { safeStringify } from "@forklaunch/common";
146
+ import {
147
+ enrichExpressLikeSend
148
+ } from "@forklaunch/core/http";
149
+ function enrichResponseTransmission(req, res, next) {
150
+ const originalSend = res.send;
151
+ const originalJson = res.json;
152
+ const originalSetHeader = res.setHeader;
153
+ res.json = function(data) {
154
+ res.statusCode = Number(res._status_code);
155
+ enrichExpressLikeSend(
156
+ this,
157
+ req,
158
+ res,
159
+ originalJson,
160
+ originalSend,
161
+ data,
162
+ !res.cors && (res._cork && !res._corked || !res._cork)
163
+ );
164
+ return data;
165
+ };
166
+ res.send = function(data) {
167
+ res.statusCode = Number(res._status_code);
168
+ if (res.sent) {
169
+ originalSend.call(this, data);
170
+ return true;
171
+ }
172
+ enrichExpressLikeSend(
173
+ this,
174
+ req,
175
+ res,
176
+ originalSend,
177
+ originalSend,
178
+ data,
179
+ !res.cors && (res._cork && !res._corked || !res._cork)
180
+ );
181
+ res.sent = true;
182
+ return true;
183
+ };
184
+ res.setHeader = function(name, value) {
185
+ let stringifiedValue;
186
+ if (Array.isArray(value)) {
187
+ stringifiedValue = value.map((v) => safeStringify(v)).join("\n");
188
+ } else {
189
+ stringifiedValue = safeStringify(value);
190
+ }
191
+ return originalSetHeader.call(this, name, stringifiedValue);
192
+ };
193
+ res.sseEmitter = function(emitter) {
194
+ const generator = emitter();
195
+ enrichExpressLikeSend(
196
+ this,
197
+ req,
198
+ res,
199
+ originalSend,
200
+ originalSend,
201
+ generator,
202
+ !res.cors && (res._cork && !res._corked || !res._cork)
203
+ );
204
+ };
205
+ next();
206
+ }
93
207
 
94
208
  // src/middleware/swagger.middleware.ts
95
209
  import LiveDirectory from "live-directory";
@@ -167,54 +281,66 @@ var Application = class extends ForklaunchExpressLikeApplication {
167
281
  * );
168
282
  * ```
169
283
  */
170
- constructor(schemaValidator, openTelemetryCollector, docsConfiguration) {
171
- super(schemaValidator, new Server(), openTelemetryCollector);
172
- this.docsConfiguration = docsConfiguration;
284
+ constructor(schemaValidator, openTelemetryCollector, configurationOptions) {
285
+ super(
286
+ schemaValidator,
287
+ new Server(configurationOptions?.server),
288
+ [
289
+ contentParse(configurationOptions),
290
+ enrichResponseTransmission
291
+ ],
292
+ openTelemetryCollector
293
+ );
294
+ this.configurationOptions = configurationOptions;
173
295
  }
174
296
  async listen(arg0, arg1, arg2) {
175
297
  if (typeof arg0 === "number") {
176
298
  const port = arg0 || Number(process.env.PORT);
177
299
  this.internal.set_error_handler((req, res, err) => {
300
+ const statusCode = Number(res.statusCode);
178
301
  res.locals.errorMessage = err.message;
179
302
  res.type("text/plain");
180
- res.status(
181
- res.statusCode && res.statusCode >= 400 ? res.statusCode : 500
182
- ).send(
303
+ res.status(statusCode && statusCode >= 400 ? statusCode : 500).send(
183
304
  `Internal server error:
184
305
 
185
306
  Correlation id: ${isForklaunchRequest(req) ? req.context.correlationId : "No correlation ID"}`
186
307
  );
187
308
  logger("error").error(err.stack ?? err.message, {
188
- [ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode ?? 500
309
+ [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode ?? 500
189
310
  });
190
311
  });
191
- if (this.docsConfiguration == null || this.docsConfiguration.type === "scalar") {
312
+ const openApi = generateSwaggerDocument(
313
+ this.schemaValidator,
314
+ port,
315
+ this.routers
316
+ );
317
+ if (this.configurationOptions?.docs == null || this.configurationOptions?.docs.type === "scalar") {
192
318
  this.internal.use(
193
319
  `/api/${process.env.VERSION ?? "v1"}${process.env.DOCS_PATH ?? "/docs"}`,
194
320
  apiReference({
195
- content: generateSwaggerDocument(
196
- this.schemaValidator,
197
- port,
198
- this.routers
199
- ),
200
- ...this.docsConfiguration
321
+ content: openApi,
322
+ ...this.configurationOptions?.docs
201
323
  })
202
324
  );
203
- } else if (this.docsConfiguration.type === "swagger") {
325
+ } else if (this.configurationOptions?.docs.type === "swagger") {
204
326
  const swaggerPath = `/api/${process.env.VERSION ?? "v1"}${process.env.DOCS_PATH ?? "/docs"}`;
205
327
  this.internal.use(swaggerPath, swaggerRedirect(swaggerPath));
206
- this.internal.get(
207
- `${swaggerPath}/*`,
208
- swagger(
209
- swaggerPath,
210
- generateSwaggerDocument(
211
- this.schemaValidator,
212
- port,
213
- this.routers
214
- )
215
- )
216
- );
328
+ this.internal.get(`${swaggerPath}/*`, swagger(swaggerPath, openApi));
217
329
  }
330
+ this.internal.get(
331
+ `/api/${process.env.VERSION ?? "v1"}/openapi`,
332
+ (_, res) => {
333
+ res.type("application/json");
334
+ res.json(openApi);
335
+ }
336
+ );
337
+ this.internal.get(
338
+ `/api/${process.env.VERSION ?? "v1"}/openapi-hash`,
339
+ async (_, res) => {
340
+ const hash = await crypto.createHash("sha256").update(safeStringify2(openApi)).digest("hex");
341
+ res.send(hash);
342
+ }
343
+ );
218
344
  if (arg1 && typeof arg1 === "string") {
219
345
  return this.internal.listen(port, arg1, arg2);
220
346
  } else if (arg1 && typeof arg1 === "function") {
@@ -236,94 +362,6 @@ import {
236
362
  Router as ExpressRouter
237
363
  } from "@forklaunch/hyper-express-fork";
238
364
 
239
- // src/middleware/contentParse.middleware.ts
240
- async function contentParse(req) {
241
- switch (req.headers["content-type"] && req.headers["content-type"].split(";")[0]) {
242
- case "application/json":
243
- req.body = await req.json();
244
- break;
245
- case "application/x-www-form-urlencoded":
246
- req.body = await req.urlencoded();
247
- break;
248
- case "text/plain":
249
- req.body = await req.text();
250
- break;
251
- case "application/octet-stream":
252
- req.body = await req.buffer();
253
- break;
254
- case "multipart/form-data":
255
- req.body = {};
256
- await req.multipart(async (field) => {
257
- if (field.file) {
258
- const fileBuffer = Buffer.from(
259
- await field.file.stream.read(),
260
- field.encoding
261
- );
262
- req.body[field.name] = {
263
- buffer: fileBuffer,
264
- name: field.file.name ?? field.name,
265
- type: field.mime_type
266
- };
267
- } else {
268
- req.body[field.name] = field.value;
269
- }
270
- });
271
- break;
272
- default:
273
- req.body = await req.json();
274
- break;
275
- }
276
- }
277
-
278
- // src/middleware/enrichResponseTransmission.middleware.ts
279
- import {
280
- enrichExpressLikeSend
281
- } from "@forklaunch/core/http";
282
- function enrichResponseTransmission(req, res, next) {
283
- const originalSend = res.send;
284
- const originalJson = res.json;
285
- const originalSetHeader = res.setHeader;
286
- res.json = function(data) {
287
- res.bodyData = data;
288
- res.statusCode = res._status_code;
289
- enrichExpressLikeSend(
290
- this,
291
- req,
292
- res,
293
- originalJson,
294
- data,
295
- !res.cors && (res._cork && !res._corked || !res._cork)
296
- );
297
- return data;
298
- };
299
- res.send = function(data) {
300
- if (!res.bodyData) {
301
- res.bodyData = data;
302
- res.statusCode = res._status_code;
303
- }
304
- return enrichExpressLikeSend(
305
- this,
306
- req,
307
- res,
308
- originalSend,
309
- data,
310
- !res.cors && (res._cork && !res._corked || !res._cork)
311
- );
312
- };
313
- res.setHeader = function(name, value) {
314
- let stringifiedValue;
315
- if (Array.isArray(value)) {
316
- stringifiedValue = value.map(
317
- (v) => typeof v !== "string" ? JSON.stringify(v) : v
318
- );
319
- } else {
320
- stringifiedValue = typeof value !== "string" ? JSON.stringify(value) : value;
321
- }
322
- return originalSetHeader.call(this, name, stringifiedValue);
323
- };
324
- next();
325
- }
326
-
327
365
  // src/middleware/polyfillGetHeaders.middleware.ts
328
366
  function polyfillGetHeaders(_req, res, next) {
329
367
  res.getHeaders = () => {
@@ -334,19 +372,19 @@ function polyfillGetHeaders(_req, res, next) {
334
372
 
335
373
  // src/hyperExpressRouter.ts
336
374
  var Router = class extends ForklaunchExpressLikeRouter {
337
- constructor(basePath, schemaValidator, openTelemetryCollector) {
375
+ constructor(basePath, schemaValidator, openTelemetryCollector, options2) {
338
376
  super(
339
377
  basePath,
340
378
  schemaValidator,
341
379
  new ExpressRouter(),
380
+ [
381
+ contentParse(options2),
382
+ enrichResponseTransmission
383
+ ],
342
384
  openTelemetryCollector
343
385
  );
344
386
  this.basePath = basePath;
345
387
  this.internal.use(polyfillGetHeaders);
346
- this.internal.use(contentParse);
347
- this.internal.use(
348
- enrichResponseTransmission
349
- );
350
388
  }
351
389
  route(path) {
352
390
  this.internal.route(path);
@@ -366,15 +404,16 @@ var Router = class extends ForklaunchExpressLikeRouter {
366
404
  };
367
405
 
368
406
  // index.ts
369
- function forklaunchExpress(schemaValidator, openTelemetryCollector, docsConfiguration) {
370
- return new Application(
407
+ function forklaunchExpress(schemaValidator, openTelemetryCollector, options2) {
408
+ return new Application(schemaValidator, openTelemetryCollector, options2);
409
+ }
410
+ function forklaunchRouter(basePath, schemaValidator, openTelemetryCollector, options2) {
411
+ const router = new Router(
412
+ basePath,
371
413
  schemaValidator,
372
414
  openTelemetryCollector,
373
- docsConfiguration
415
+ options2
374
416
  );
375
- }
376
- function forklaunchRouter(basePath, schemaValidator, openTelemetryCollector) {
377
- const router = new Router(basePath, schemaValidator, openTelemetryCollector);
378
417
  return router;
379
418
  }
380
419
  var handlers = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/hyper-express",
3
- "version": "0.4.11",
3
+ "version": "0.5.2",
4
4
  "description": "Forklaunch framework for hyper-express.",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {
@@ -25,36 +25,37 @@
25
25
  "lib/**"
26
26
  ],
27
27
  "dependencies": {
28
- "@forklaunch/hyper-express-fork": "^6.17.30",
29
- "@scalar/express-api-reference": "^0.6.8",
28
+ "@forklaunch/hyper-express-fork": "^6.17.34",
29
+ "@scalar/express-api-reference": "^0.8.1",
30
30
  "cors": "^2.8.5",
31
31
  "live-directory": "^3.0.3",
32
32
  "openapi3-ts": "^4.4.0",
33
33
  "qs": "^6.14.0",
34
- "swagger-ui-dist": "^5.21.0",
34
+ "swagger-ui-dist": "^5.22.0",
35
35
  "swagger-ui-express": "^5.0.1",
36
36
  "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.44.0",
37
- "@forklaunch/common": "0.2.11",
38
- "@forklaunch/core": "0.7.4",
39
- "@forklaunch/validator": "0.5.4"
37
+ "@forklaunch/common": "0.3.2",
38
+ "@forklaunch/validator": "0.6.2",
39
+ "@forklaunch/core": "0.8.2"
40
40
  },
41
41
  "devDependencies": {
42
- "@eslint/js": "^9.25.0",
43
- "@types/cors": "^2.8.17",
42
+ "@eslint/js": "^9.27.0",
43
+ "@types/busboy": "^1.5.4",
44
+ "@types/cors": "^2.8.18",
44
45
  "@types/jest": "^29.5.14",
45
- "@types/qs": "^6.9.18",
46
+ "@types/qs": "^6.14.0",
46
47
  "@types/swagger-ui-dist": "^3.30.5",
47
48
  "@types/swagger-ui-express": "^4.1.8",
48
49
  "jest": "^29.7.0",
49
50
  "kill-port-process": "^3.2.1",
50
51
  "prettier": "^3.5.3",
51
- "ts-jest": "^29.3.2",
52
+ "ts-jest": "^29.3.4",
52
53
  "ts-node": "^10.9.2",
53
- "tsup": "^8.4.0",
54
- "tsx": "^4.19.3",
55
- "typedoc": "^0.28.3",
54
+ "tsup": "^8.5.0",
55
+ "tsx": "^4.19.4",
56
+ "typedoc": "^0.28.5",
56
57
  "typescript": "^5.8.3",
57
- "typescript-eslint": "^8.30.1"
58
+ "typescript-eslint": "^8.33.0"
58
59
  },
59
60
  "scripts": {
60
61
  "build": "tsc --noEmit && tsup index.ts --format cjs,esm --no-splitting --dts --tsconfig tsconfig.json --out-dir lib --clean",