@extk/expressive 0.8.0 → 0.10.0

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/dist/index.d.mts CHANGED
@@ -1,13 +1,14 @@
1
1
  import * as express from 'express';
2
2
  import express__default, { RequestHandler } from 'express';
3
3
  import * as express_serve_static_core from 'express-serve-static-core';
4
- import { RouteParameters } from 'express-serve-static-core';
4
+ import { RouteParameters, Query } from 'express-serve-static-core';
5
5
  import { HelmetOptions } from 'helmet';
6
6
  import morgan from 'morgan';
7
+ import { SwaggerUiOptions, SwaggerOptions } from 'swagger-ui-express';
7
8
 
8
9
  type ExpressRoute = string;
9
10
  type ExpressLocalsObj = Record<string, any>;
10
- type ExpressHandler = RequestHandler<RouteParameters<ExpressRoute>, any, any, qs.ParsedQs, ExpressLocalsObj[]>;
11
+ type ExpressHandler = RequestHandler<RouteParameters<ExpressRoute>, any, any, Query, ExpressLocalsObj>;
11
12
  type PaginationQuery = {
12
13
  limit?: string | number;
13
14
  page?: string | number;
@@ -187,25 +188,68 @@ type SwaggerConfig = {
187
188
  security?: AuthMethod[];
188
189
  };
189
190
 
190
- type SwaggerOptions = {
191
- path?: ExpressRoute;
192
- config: SwaggerConfig;
191
+ declare class SwaggerBuilder {
192
+ private swaggerDoc;
193
+ constructor(swaggerDoc: SwaggerConfig);
194
+ withInfo(info: SwaggerConfig['info']): this;
195
+ withServers(servers: Servers): this;
196
+ withSecuritySchemes(schemes: Record<string, SecurityScheme>): this;
197
+ withSchemas(schemas: Record<string, Schema>): this;
198
+ withDefaultSecurity(globalAuthMethods: AuthMethod[]): this;
199
+ }
200
+ declare function singleFileSchema(field?: string, required?: boolean): Content;
201
+ declare function formDataSchema(schema: Schema): Content;
202
+ declare function jsonSchema(schema: Schema): Content;
203
+ declare function jsonSchemaRef(name: string): Content;
204
+ declare function param(inP: Param['in'], id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
205
+ declare function pathParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
206
+ declare function queryParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
207
+ declare function headerParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
208
+ declare const SWG: {
209
+ param: typeof param;
210
+ pathParam: typeof pathParam;
211
+ queryParam: typeof queryParam;
212
+ headerParam: typeof headerParam;
213
+ formDataSchema: typeof formDataSchema;
214
+ jsonSchema: typeof jsonSchema;
215
+ jsonSchemaRef: typeof jsonSchemaRef;
216
+ singleFileSchema: typeof singleFileSchema;
217
+ security: (name: string) => AuthMethod;
218
+ securitySchemes: {
219
+ readonly BasicAuth: () => SecurityScheme;
220
+ readonly BearerAuth: () => SecurityScheme;
221
+ readonly ApiKeyAuth: (headerName: string) => SecurityScheme;
222
+ readonly OpenID: (openIdConnectUrl: string) => SecurityScheme;
223
+ readonly OAuth2: (authorizationUrl: string, tokenUrl: string, scopes: Record<string, string>) => SecurityScheme;
224
+ };
225
+ };
226
+
227
+ type SwaggerRef = {
228
+ doc: SwaggerConfig | null;
229
+ };
230
+ type ExpressiveSwaggerOptions = {
231
+ path: ExpressRoute;
232
+ uiOpts?: SwaggerUiOptions;
233
+ options?: SwaggerOptions;
234
+ customCss?: string;
235
+ customfavIcon?: string;
236
+ swaggerUrl?: string;
237
+ customSiteTitle?: string;
193
238
  };
194
239
  declare class ServerBuilder {
195
240
  private app;
196
241
  private container;
197
- constructor(app: express__default.Express, container: Container);
242
+ private swaggerRef;
243
+ constructor(app: express__default.Express, container: Container, swaggerRef: SwaggerRef);
198
244
  build(): express__default.Express;
199
245
  withHelmet(options?: Readonly<HelmetOptions>): this;
200
- withQs(): this;
201
- withMorgan(format?: string, // TODO: FormatFn
202
- options?: Parameters<typeof morgan>[1]): this;
246
+ withMorgan(format?: string | morgan.FormatFn, options?: Parameters<typeof morgan>[1]): this;
203
247
  withRoutes(routes: express__default.Router): this;
204
248
  /**
205
249
  * Helper function for fluent design
206
250
  */
207
251
  with(fn: (app: express__default.Express, container: Container) => void): this;
208
- withSwagger(swagger: SwaggerOptions, ...handlers: ExpressHandler[]): this;
252
+ withSwagger(configure: (builder: SwaggerBuilder) => void, opts: ExpressiveSwaggerOptions, ...handlers: ExpressHandler[]): this;
209
253
  }
210
254
 
211
255
  declare class ApiError extends Error {
@@ -252,43 +296,6 @@ declare class UserUnauthorizedError extends ApiError {
252
296
  constructor(message?: string);
253
297
  }
254
298
 
255
- declare class SwaggerBuilder {
256
- private swaggerDoc;
257
- constructor(swaggerDoc: SwaggerConfig);
258
- withInfo(info: SwaggerConfig['info']): this;
259
- withServers(servers: Servers): this;
260
- withSecuritySchemes(schemes: Record<string, SecurityScheme>): this;
261
- withSchemas(schemas: Record<string, Schema>): this;
262
- withDefaultSecurity(globalAuthMethods: AuthMethod[]): this;
263
- build(): SwaggerConfig;
264
- }
265
- declare function singleFileSchema(field?: string, required?: boolean): Content;
266
- declare function formDataSchema(schema: Schema): Content;
267
- declare function jsonSchema(schema: Schema): Content;
268
- declare function jsonSchemaRef(name: string): Content;
269
- declare function param(inP: Param['in'], id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
270
- declare function pathParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
271
- declare function queryParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
272
- declare function headerParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
273
- declare const SWG: {
274
- param: typeof param;
275
- pathParam: typeof pathParam;
276
- queryParam: typeof queryParam;
277
- headerParam: typeof headerParam;
278
- formDataSchema: typeof formDataSchema;
279
- jsonSchema: typeof jsonSchema;
280
- jsonSchemaRef: typeof jsonSchemaRef;
281
- singleFileSchema: typeof singleFileSchema;
282
- security: (name: string) => AuthMethod;
283
- securitySchemes: {
284
- readonly BasicAuth: () => SecurityScheme;
285
- readonly BearerAuth: () => SecurityScheme;
286
- readonly ApiKeyAuth: (headerName: string) => SecurityScheme;
287
- readonly OpenID: (openIdConnectUrl: string) => SecurityScheme;
288
- readonly OAuth2: (authorizationUrl: string, tokenUrl: string, scopes: Record<string, string>) => SecurityScheme;
289
- };
290
- };
291
-
292
299
  declare function parsePositiveInteger<T>(v: T, defaultValue: number, max?: number): number;
293
300
  declare function parseIdOrFail(v: unknown): number;
294
301
  declare function slugify(text: string): string;
@@ -317,7 +324,6 @@ declare class ApiResponse<T = undefined> {
317
324
  }
318
325
 
319
326
  declare function bootstrap(container: Container): {
320
- swaggerBuilder: () => SwaggerBuilder;
321
327
  silently: (fn: () => Promise<void> | void) => Promise<void>;
322
328
  getGlobalNotFoundMiddleware: (content?: string) => (_req: express.Request, res: express.Response, _next: express.NextFunction) => void;
323
329
  getApiNotFoundMiddleware: () => (req: express.Request, res: express.Response, _next: express.NextFunction) => void;
@@ -348,4 +354,4 @@ declare function bootstrap(container: Container): {
348
354
  };
349
355
  };
350
356
 
351
- export { type AlertHandler, ApiError, ApiErrorResponse, ApiResponse, type AuthMethod, BadRequestError, type Container, type Content, type ContentType, DuplicateError, type Env, type ExpressHandler, type ExpressLocalsObj, type ExpressRoute, FileTooBigError, ForbiddenError, type HttpMethod, InternalError, InvalidCredentialsError, InvalidFileTypeError, type Logger, NotFoundError, type OtherString, type OtherUnknown, type Pagination, type PaginationQuery, type Param, type PathItem, SWG, type Schema, SchemaValidationError, type SecurityScheme, type Servers, type SwaggerConfig, TokenExpiredError, TooManyRequestsError, UserUnauthorizedError, bootstrap, getEnvVar, getTmpDir, getTmpPath, isDev, isProd, parseDefaultPagination, parseIdOrFail, parsePositiveInteger, slugify };
357
+ export { type AlertHandler, ApiError, ApiErrorResponse, ApiResponse, type AuthMethod, BadRequestError, type Container, type Content, type ContentType, DuplicateError, type Env, type ExpressHandler, type ExpressLocalsObj, type ExpressRoute, FileTooBigError, ForbiddenError, type HttpMethod, InternalError, InvalidCredentialsError, InvalidFileTypeError, type Logger, NotFoundError, type OtherString, type OtherUnknown, type Pagination, type PaginationQuery, type Param, type PathItem, SWG, type Schema, SchemaValidationError, type SecurityScheme, type Servers, SwaggerBuilder, type SwaggerConfig, TokenExpiredError, TooManyRequestsError, UserUnauthorizedError, bootstrap, getEnvVar, getTmpDir, getTmpPath, isDev, isProd, parseDefaultPagination, parseIdOrFail, parsePositiveInteger, slugify };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import * as express from 'express';
2
2
  import express__default, { RequestHandler } from 'express';
3
3
  import * as express_serve_static_core from 'express-serve-static-core';
4
- import { RouteParameters } from 'express-serve-static-core';
4
+ import { RouteParameters, Query } from 'express-serve-static-core';
5
5
  import { HelmetOptions } from 'helmet';
6
6
  import morgan from 'morgan';
7
+ import { SwaggerUiOptions, SwaggerOptions } from 'swagger-ui-express';
7
8
 
8
9
  type ExpressRoute = string;
9
10
  type ExpressLocalsObj = Record<string, any>;
10
- type ExpressHandler = RequestHandler<RouteParameters<ExpressRoute>, any, any, qs.ParsedQs, ExpressLocalsObj[]>;
11
+ type ExpressHandler = RequestHandler<RouteParameters<ExpressRoute>, any, any, Query, ExpressLocalsObj>;
11
12
  type PaginationQuery = {
12
13
  limit?: string | number;
13
14
  page?: string | number;
@@ -187,25 +188,68 @@ type SwaggerConfig = {
187
188
  security?: AuthMethod[];
188
189
  };
189
190
 
190
- type SwaggerOptions = {
191
- path?: ExpressRoute;
192
- config: SwaggerConfig;
191
+ declare class SwaggerBuilder {
192
+ private swaggerDoc;
193
+ constructor(swaggerDoc: SwaggerConfig);
194
+ withInfo(info: SwaggerConfig['info']): this;
195
+ withServers(servers: Servers): this;
196
+ withSecuritySchemes(schemes: Record<string, SecurityScheme>): this;
197
+ withSchemas(schemas: Record<string, Schema>): this;
198
+ withDefaultSecurity(globalAuthMethods: AuthMethod[]): this;
199
+ }
200
+ declare function singleFileSchema(field?: string, required?: boolean): Content;
201
+ declare function formDataSchema(schema: Schema): Content;
202
+ declare function jsonSchema(schema: Schema): Content;
203
+ declare function jsonSchemaRef(name: string): Content;
204
+ declare function param(inP: Param['in'], id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
205
+ declare function pathParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
206
+ declare function queryParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
207
+ declare function headerParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
208
+ declare const SWG: {
209
+ param: typeof param;
210
+ pathParam: typeof pathParam;
211
+ queryParam: typeof queryParam;
212
+ headerParam: typeof headerParam;
213
+ formDataSchema: typeof formDataSchema;
214
+ jsonSchema: typeof jsonSchema;
215
+ jsonSchemaRef: typeof jsonSchemaRef;
216
+ singleFileSchema: typeof singleFileSchema;
217
+ security: (name: string) => AuthMethod;
218
+ securitySchemes: {
219
+ readonly BasicAuth: () => SecurityScheme;
220
+ readonly BearerAuth: () => SecurityScheme;
221
+ readonly ApiKeyAuth: (headerName: string) => SecurityScheme;
222
+ readonly OpenID: (openIdConnectUrl: string) => SecurityScheme;
223
+ readonly OAuth2: (authorizationUrl: string, tokenUrl: string, scopes: Record<string, string>) => SecurityScheme;
224
+ };
225
+ };
226
+
227
+ type SwaggerRef = {
228
+ doc: SwaggerConfig | null;
229
+ };
230
+ type ExpressiveSwaggerOptions = {
231
+ path: ExpressRoute;
232
+ uiOpts?: SwaggerUiOptions;
233
+ options?: SwaggerOptions;
234
+ customCss?: string;
235
+ customfavIcon?: string;
236
+ swaggerUrl?: string;
237
+ customSiteTitle?: string;
193
238
  };
194
239
  declare class ServerBuilder {
195
240
  private app;
196
241
  private container;
197
- constructor(app: express__default.Express, container: Container);
242
+ private swaggerRef;
243
+ constructor(app: express__default.Express, container: Container, swaggerRef: SwaggerRef);
198
244
  build(): express__default.Express;
199
245
  withHelmet(options?: Readonly<HelmetOptions>): this;
200
- withQs(): this;
201
- withMorgan(format?: string, // TODO: FormatFn
202
- options?: Parameters<typeof morgan>[1]): this;
246
+ withMorgan(format?: string | morgan.FormatFn, options?: Parameters<typeof morgan>[1]): this;
203
247
  withRoutes(routes: express__default.Router): this;
204
248
  /**
205
249
  * Helper function for fluent design
206
250
  */
207
251
  with(fn: (app: express__default.Express, container: Container) => void): this;
208
- withSwagger(swagger: SwaggerOptions, ...handlers: ExpressHandler[]): this;
252
+ withSwagger(configure: (builder: SwaggerBuilder) => void, opts: ExpressiveSwaggerOptions, ...handlers: ExpressHandler[]): this;
209
253
  }
210
254
 
211
255
  declare class ApiError extends Error {
@@ -252,43 +296,6 @@ declare class UserUnauthorizedError extends ApiError {
252
296
  constructor(message?: string);
253
297
  }
254
298
 
255
- declare class SwaggerBuilder {
256
- private swaggerDoc;
257
- constructor(swaggerDoc: SwaggerConfig);
258
- withInfo(info: SwaggerConfig['info']): this;
259
- withServers(servers: Servers): this;
260
- withSecuritySchemes(schemes: Record<string, SecurityScheme>): this;
261
- withSchemas(schemas: Record<string, Schema>): this;
262
- withDefaultSecurity(globalAuthMethods: AuthMethod[]): this;
263
- build(): SwaggerConfig;
264
- }
265
- declare function singleFileSchema(field?: string, required?: boolean): Content;
266
- declare function formDataSchema(schema: Schema): Content;
267
- declare function jsonSchema(schema: Schema): Content;
268
- declare function jsonSchemaRef(name: string): Content;
269
- declare function param(inP: Param['in'], id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
270
- declare function pathParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
271
- declare function queryParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
272
- declare function headerParam(id: string, schema: Schema, required?: boolean, description?: string, name?: string): Param;
273
- declare const SWG: {
274
- param: typeof param;
275
- pathParam: typeof pathParam;
276
- queryParam: typeof queryParam;
277
- headerParam: typeof headerParam;
278
- formDataSchema: typeof formDataSchema;
279
- jsonSchema: typeof jsonSchema;
280
- jsonSchemaRef: typeof jsonSchemaRef;
281
- singleFileSchema: typeof singleFileSchema;
282
- security: (name: string) => AuthMethod;
283
- securitySchemes: {
284
- readonly BasicAuth: () => SecurityScheme;
285
- readonly BearerAuth: () => SecurityScheme;
286
- readonly ApiKeyAuth: (headerName: string) => SecurityScheme;
287
- readonly OpenID: (openIdConnectUrl: string) => SecurityScheme;
288
- readonly OAuth2: (authorizationUrl: string, tokenUrl: string, scopes: Record<string, string>) => SecurityScheme;
289
- };
290
- };
291
-
292
299
  declare function parsePositiveInteger<T>(v: T, defaultValue: number, max?: number): number;
293
300
  declare function parseIdOrFail(v: unknown): number;
294
301
  declare function slugify(text: string): string;
@@ -317,7 +324,6 @@ declare class ApiResponse<T = undefined> {
317
324
  }
318
325
 
319
326
  declare function bootstrap(container: Container): {
320
- swaggerBuilder: () => SwaggerBuilder;
321
327
  silently: (fn: () => Promise<void> | void) => Promise<void>;
322
328
  getGlobalNotFoundMiddleware: (content?: string) => (_req: express.Request, res: express.Response, _next: express.NextFunction) => void;
323
329
  getApiNotFoundMiddleware: () => (req: express.Request, res: express.Response, _next: express.NextFunction) => void;
@@ -348,4 +354,4 @@ declare function bootstrap(container: Container): {
348
354
  };
349
355
  };
350
356
 
351
- export { type AlertHandler, ApiError, ApiErrorResponse, ApiResponse, type AuthMethod, BadRequestError, type Container, type Content, type ContentType, DuplicateError, type Env, type ExpressHandler, type ExpressLocalsObj, type ExpressRoute, FileTooBigError, ForbiddenError, type HttpMethod, InternalError, InvalidCredentialsError, InvalidFileTypeError, type Logger, NotFoundError, type OtherString, type OtherUnknown, type Pagination, type PaginationQuery, type Param, type PathItem, SWG, type Schema, SchemaValidationError, type SecurityScheme, type Servers, type SwaggerConfig, TokenExpiredError, TooManyRequestsError, UserUnauthorizedError, bootstrap, getEnvVar, getTmpDir, getTmpPath, isDev, isProd, parseDefaultPagination, parseIdOrFail, parsePositiveInteger, slugify };
357
+ export { type AlertHandler, ApiError, ApiErrorResponse, ApiResponse, type AuthMethod, BadRequestError, type Container, type Content, type ContentType, DuplicateError, type Env, type ExpressHandler, type ExpressLocalsObj, type ExpressRoute, FileTooBigError, ForbiddenError, type HttpMethod, InternalError, InvalidCredentialsError, InvalidFileTypeError, type Logger, NotFoundError, type OtherString, type OtherUnknown, type Pagination, type PaginationQuery, type Param, type PathItem, SWG, type Schema, SchemaValidationError, type SecurityScheme, type Servers, SwaggerBuilder, type SwaggerConfig, TokenExpiredError, TooManyRequestsError, UserUnauthorizedError, bootstrap, getEnvVar, getTmpDir, getTmpPath, isDev, isProd, parseDefaultPagination, parseIdOrFail, parsePositiveInteger, slugify };
package/dist/index.js CHANGED
@@ -63,7 +63,6 @@ module.exports = __toCommonJS(index_exports);
63
63
  var import_express = __toESM(require("express"));
64
64
  var import_helmet = __toESM(require("helmet"));
65
65
  var import_morgan = __toESM(require("morgan"));
66
- var import_qs = __toESM(require("qs"));
67
66
  var import_swagger_ui_express = __toESM(require("swagger-ui-express"));
68
67
 
69
68
  // src/swagger.ts
@@ -91,9 +90,6 @@ var SwaggerBuilder = class {
91
90
  this.swaggerDoc.security = globalAuthMethods;
92
91
  return this;
93
92
  }
94
- build() {
95
- return this.swaggerDoc;
96
- }
97
93
  };
98
94
  var securitySchemes = {
99
95
  BasicAuth: () => ({
@@ -211,28 +207,22 @@ var SWG = {
211
207
 
212
208
  // src/expressive.ts
213
209
  var ServerBuilder = class {
214
- constructor(app, container) {
210
+ constructor(app, container, swaggerRef) {
215
211
  this.app = app;
216
212
  this.container = container;
213
+ this.swaggerRef = swaggerRef;
217
214
  }
218
215
  build() {
216
+ this.swaggerRef.doc = null;
219
217
  return this.app;
220
218
  }
221
219
  withHelmet(options) {
222
220
  this.app.use((0, import_helmet.default)(options ?? {}));
223
221
  return this;
224
222
  }
225
- withQs() {
226
- this.app.set("query parser", function(str) {
227
- return import_qs.default.parse(str, { decoder(s) {
228
- return decodeURIComponent(s);
229
- } });
230
- });
231
- return this;
232
- }
233
223
  withMorgan(format, options) {
234
224
  this.app.use((0, import_morgan.default)(
235
- format ?? ":req[x-real-ip] :method :url :status :res[content-length] - :response-time ms",
225
+ format ?? ":remote-addr :method :url :status :res[content-length] - :response-time ms",
236
226
  options ?? { stream: { write: (message) => {
237
227
  this.container.logger.info(message.trim());
238
228
  } } }
@@ -250,18 +240,45 @@ var ServerBuilder = class {
250
240
  fn(this.app, this.container);
251
241
  return this;
252
242
  }
253
- withSwagger(swagger, ...handlers) {
254
- this.app.use(swagger.path ?? "/api-docs", ...handlers, import_swagger_ui_express.default.serve, import_swagger_ui_express.default.setup(swagger.config, {
255
- customSiteTitle: swagger.config.info?.title
256
- }));
243
+ withSwagger(configure, opts, ...handlers) {
244
+ const config = this.swaggerRef.doc;
245
+ if (!config) throw new Error("withSwagger must be called before build()");
246
+ configure(new SwaggerBuilder(config));
247
+ const { path: path2, uiOpts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle } = opts;
248
+ const uiOptsWithDefaults = {
249
+ customSiteTitle: config.info?.title,
250
+ ...uiOpts
251
+ };
252
+ this.app.use(
253
+ path2,
254
+ ...handlers,
255
+ import_swagger_ui_express.default.serve,
256
+ import_swagger_ui_express.default.setup(
257
+ config,
258
+ uiOptsWithDefaults,
259
+ options,
260
+ customCss,
261
+ customfavIcon,
262
+ swaggerUrl,
263
+ customSiteTitle
264
+ )
265
+ );
257
266
  return this;
258
267
  }
259
268
  };
260
- function buildExpressive(container, swaggerDoc) {
269
+ function buildExpressive(container) {
270
+ const swaggerRef = {
271
+ doc: {
272
+ openapi: "3.1.0",
273
+ info: {},
274
+ paths: {},
275
+ components: {}
276
+ }
277
+ };
261
278
  return {
262
279
  expressiveServer(configs) {
263
280
  const app = configs?.app ?? (0, import_express.default)();
264
- return new ServerBuilder(app, container);
281
+ return new ServerBuilder(app, container, swaggerRef);
265
282
  },
266
283
  expressiveRouter(configs) {
267
284
  const router = import_express.default.Router();
@@ -304,10 +321,12 @@ function buildExpressive(container, swaggerDoc) {
304
321
  } else {
305
322
  pathItem.parameters.push(...tryParsePathParameters(route));
306
323
  }
307
- if (!swaggerDoc.paths[route]) {
308
- swaggerDoc.paths[route] = {};
324
+ if (swaggerRef.doc) {
325
+ if (!swaggerRef.doc.paths[route]) {
326
+ swaggerRef.doc.paths[route] = {};
327
+ }
328
+ swaggerRef.doc.paths[route][context.method] = pathItem;
309
329
  }
310
- swaggerDoc.paths[route][context.method] = pathItem;
311
330
  return router;
312
331
  }
313
332
  };
@@ -545,17 +564,9 @@ var ApiResponse = class {
545
564
 
546
565
  // src/index.ts
547
566
  function bootstrap(container) {
548
- const swaggerDoc = {
549
- openapi: "3.1.0",
550
- // TODO
551
- info: {},
552
- paths: {},
553
- components: {}
554
- };
555
567
  return {
556
- ...buildExpressive(container, swaggerDoc),
568
+ ...buildExpressive(container),
557
569
  ...buildMiddleware(container),
558
- swaggerBuilder: () => new SwaggerBuilder(swaggerDoc),
559
570
  silently: async (fn) => {
560
571
  try {
561
572
  await fn();
package/dist/index.mjs CHANGED
@@ -2,7 +2,6 @@
2
2
  import express from "express";
3
3
  import helmet from "helmet";
4
4
  import morgan from "morgan";
5
- import qs from "qs";
6
5
  import swaggerUi from "swagger-ui-express";
7
6
 
8
7
  // src/swagger.ts
@@ -30,9 +29,6 @@ var SwaggerBuilder = class {
30
29
  this.swaggerDoc.security = globalAuthMethods;
31
30
  return this;
32
31
  }
33
- build() {
34
- return this.swaggerDoc;
35
- }
36
32
  };
37
33
  var securitySchemes = {
38
34
  BasicAuth: () => ({
@@ -150,28 +146,22 @@ var SWG = {
150
146
 
151
147
  // src/expressive.ts
152
148
  var ServerBuilder = class {
153
- constructor(app, container) {
149
+ constructor(app, container, swaggerRef) {
154
150
  this.app = app;
155
151
  this.container = container;
152
+ this.swaggerRef = swaggerRef;
156
153
  }
157
154
  build() {
155
+ this.swaggerRef.doc = null;
158
156
  return this.app;
159
157
  }
160
158
  withHelmet(options) {
161
159
  this.app.use(helmet(options ?? {}));
162
160
  return this;
163
161
  }
164
- withQs() {
165
- this.app.set("query parser", function(str) {
166
- return qs.parse(str, { decoder(s) {
167
- return decodeURIComponent(s);
168
- } });
169
- });
170
- return this;
171
- }
172
162
  withMorgan(format, options) {
173
163
  this.app.use(morgan(
174
- format ?? ":req[x-real-ip] :method :url :status :res[content-length] - :response-time ms",
164
+ format ?? ":remote-addr :method :url :status :res[content-length] - :response-time ms",
175
165
  options ?? { stream: { write: (message) => {
176
166
  this.container.logger.info(message.trim());
177
167
  } } }
@@ -189,18 +179,45 @@ var ServerBuilder = class {
189
179
  fn(this.app, this.container);
190
180
  return this;
191
181
  }
192
- withSwagger(swagger, ...handlers) {
193
- this.app.use(swagger.path ?? "/api-docs", ...handlers, swaggerUi.serve, swaggerUi.setup(swagger.config, {
194
- customSiteTitle: swagger.config.info?.title
195
- }));
182
+ withSwagger(configure, opts, ...handlers) {
183
+ const config = this.swaggerRef.doc;
184
+ if (!config) throw new Error("withSwagger must be called before build()");
185
+ configure(new SwaggerBuilder(config));
186
+ const { path: path2, uiOpts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle } = opts;
187
+ const uiOptsWithDefaults = {
188
+ customSiteTitle: config.info?.title,
189
+ ...uiOpts
190
+ };
191
+ this.app.use(
192
+ path2,
193
+ ...handlers,
194
+ swaggerUi.serve,
195
+ swaggerUi.setup(
196
+ config,
197
+ uiOptsWithDefaults,
198
+ options,
199
+ customCss,
200
+ customfavIcon,
201
+ swaggerUrl,
202
+ customSiteTitle
203
+ )
204
+ );
196
205
  return this;
197
206
  }
198
207
  };
199
- function buildExpressive(container, swaggerDoc) {
208
+ function buildExpressive(container) {
209
+ const swaggerRef = {
210
+ doc: {
211
+ openapi: "3.1.0",
212
+ info: {},
213
+ paths: {},
214
+ components: {}
215
+ }
216
+ };
200
217
  return {
201
218
  expressiveServer(configs) {
202
219
  const app = configs?.app ?? express();
203
- return new ServerBuilder(app, container);
220
+ return new ServerBuilder(app, container, swaggerRef);
204
221
  },
205
222
  expressiveRouter(configs) {
206
223
  const router = express.Router();
@@ -243,10 +260,12 @@ function buildExpressive(container, swaggerDoc) {
243
260
  } else {
244
261
  pathItem.parameters.push(...tryParsePathParameters(route));
245
262
  }
246
- if (!swaggerDoc.paths[route]) {
247
- swaggerDoc.paths[route] = {};
263
+ if (swaggerRef.doc) {
264
+ if (!swaggerRef.doc.paths[route]) {
265
+ swaggerRef.doc.paths[route] = {};
266
+ }
267
+ swaggerRef.doc.paths[route][context.method] = pathItem;
248
268
  }
249
- swaggerDoc.paths[route][context.method] = pathItem;
250
269
  return router;
251
270
  }
252
271
  };
@@ -484,17 +503,9 @@ var ApiResponse = class {
484
503
 
485
504
  // src/index.ts
486
505
  function bootstrap(container) {
487
- const swaggerDoc = {
488
- openapi: "3.1.0",
489
- // TODO
490
- info: {},
491
- paths: {},
492
- components: {}
493
- };
494
506
  return {
495
- ...buildExpressive(container, swaggerDoc),
507
+ ...buildExpressive(container),
496
508
  ...buildMiddleware(container),
497
- swaggerBuilder: () => new SwaggerBuilder(swaggerDoc),
498
509
  silently: async (fn) => {
499
510
  try {
500
511
  await fn();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@extk/expressive",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "type": "commonjs",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -25,7 +25,7 @@
25
25
  "lint": "eslint ./"
26
26
  },
27
27
  "devDependencies": {
28
- "@extk/eslint-config": "^0.2.1",
28
+ "@extk/eslint-config": "^0.3.0",
29
29
  "@extk/tsconfig": "0.1.1",
30
30
  "@types/chance": "^1.1.7",
31
31
  "@types/express": "^5.0.1",
@@ -33,7 +33,7 @@
33
33
  "@types/node": "^20.9.3",
34
34
  "@types/swagger-ui-express": "^4.1.8",
35
35
  "chance": "^1.1.13",
36
- "eslint": "^9.36.0",
36
+ "eslint": "^10.0.3",
37
37
  "tsup": "^8.5.0",
38
38
  "tsx": "^4.20.3",
39
39
  "typescript": "^5.9.2"
@@ -45,7 +45,6 @@
45
45
  "dotenv": "^17.1.0",
46
46
  "helmet": "^8.0.0",
47
47
  "morgan": "^1.10.0",
48
- "qs": "^6.14.1",
49
48
  "swagger-ui-express": "^5.0.1"
50
49
  },
51
50
  "author": "",