@extk/expressive 0.8.0 → 0.9.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
@@ -4,6 +4,7 @@ import * as express_serve_static_core from 'express-serve-static-core';
4
4
  import { RouteParameters } 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>;
@@ -187,14 +188,59 @@ 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
246
  withQs(): this;
@@ -205,7 +251,7 @@ declare class ServerBuilder {
205
251
  * Helper function for fluent design
206
252
  */
207
253
  with(fn: (app: express__default.Express, container: Container) => void): this;
208
- withSwagger(swagger: SwaggerOptions, ...handlers: ExpressHandler[]): this;
254
+ withSwagger(configure: (builder: SwaggerBuilder) => void, opts: ExpressiveSwaggerOptions, ...handlers: ExpressHandler[]): this;
209
255
  }
210
256
 
211
257
  declare class ApiError extends Error {
@@ -252,43 +298,6 @@ declare class UserUnauthorizedError extends ApiError {
252
298
  constructor(message?: string);
253
299
  }
254
300
 
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
301
  declare function parsePositiveInteger<T>(v: T, defaultValue: number, max?: number): number;
293
302
  declare function parseIdOrFail(v: unknown): number;
294
303
  declare function slugify(text: string): string;
@@ -317,7 +326,6 @@ declare class ApiResponse<T = undefined> {
317
326
  }
318
327
 
319
328
  declare function bootstrap(container: Container): {
320
- swaggerBuilder: () => SwaggerBuilder;
321
329
  silently: (fn: () => Promise<void> | void) => Promise<void>;
322
330
  getGlobalNotFoundMiddleware: (content?: string) => (_req: express.Request, res: express.Response, _next: express.NextFunction) => void;
323
331
  getApiNotFoundMiddleware: () => (req: express.Request, res: express.Response, _next: express.NextFunction) => void;
@@ -348,4 +356,4 @@ declare function bootstrap(container: Container): {
348
356
  };
349
357
  };
350
358
 
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 };
359
+ 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
@@ -4,6 +4,7 @@ import * as express_serve_static_core from 'express-serve-static-core';
4
4
  import { RouteParameters } 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>;
@@ -187,14 +188,59 @@ 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
246
  withQs(): this;
@@ -205,7 +251,7 @@ declare class ServerBuilder {
205
251
  * Helper function for fluent design
206
252
  */
207
253
  with(fn: (app: express__default.Express, container: Container) => void): this;
208
- withSwagger(swagger: SwaggerOptions, ...handlers: ExpressHandler[]): this;
254
+ withSwagger(configure: (builder: SwaggerBuilder) => void, opts: ExpressiveSwaggerOptions, ...handlers: ExpressHandler[]): this;
209
255
  }
210
256
 
211
257
  declare class ApiError extends Error {
@@ -252,43 +298,6 @@ declare class UserUnauthorizedError extends ApiError {
252
298
  constructor(message?: string);
253
299
  }
254
300
 
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
301
  declare function parsePositiveInteger<T>(v: T, defaultValue: number, max?: number): number;
293
302
  declare function parseIdOrFail(v: unknown): number;
294
303
  declare function slugify(text: string): string;
@@ -317,7 +326,6 @@ declare class ApiResponse<T = undefined> {
317
326
  }
318
327
 
319
328
  declare function bootstrap(container: Container): {
320
- swaggerBuilder: () => SwaggerBuilder;
321
329
  silently: (fn: () => Promise<void> | void) => Promise<void>;
322
330
  getGlobalNotFoundMiddleware: (content?: string) => (_req: express.Request, res: express.Response, _next: express.NextFunction) => void;
323
331
  getApiNotFoundMiddleware: () => (req: express.Request, res: express.Response, _next: express.NextFunction) => void;
@@ -348,4 +356,4 @@ declare function bootstrap(container: Container): {
348
356
  };
349
357
  };
350
358
 
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 };
359
+ 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
@@ -91,9 +91,6 @@ var SwaggerBuilder = class {
91
91
  this.swaggerDoc.security = globalAuthMethods;
92
92
  return this;
93
93
  }
94
- build() {
95
- return this.swaggerDoc;
96
- }
97
94
  };
98
95
  var securitySchemes = {
99
96
  BasicAuth: () => ({
@@ -211,11 +208,13 @@ var SWG = {
211
208
 
212
209
  // src/expressive.ts
213
210
  var ServerBuilder = class {
214
- constructor(app, container) {
211
+ constructor(app, container, swaggerRef) {
215
212
  this.app = app;
216
213
  this.container = container;
214
+ this.swaggerRef = swaggerRef;
217
215
  }
218
216
  build() {
217
+ this.swaggerRef.doc = null;
219
218
  return this.app;
220
219
  }
221
220
  withHelmet(options) {
@@ -233,6 +232,7 @@ var ServerBuilder = class {
233
232
  withMorgan(format, options) {
234
233
  this.app.use((0, import_morgan.default)(
235
234
  format ?? ":req[x-real-ip] :method :url :status :res[content-length] - :response-time ms",
235
+ // TODO: ip or x-real-ip; also, default to json?
236
236
  options ?? { stream: { write: (message) => {
237
237
  this.container.logger.info(message.trim());
238
238
  } } }
@@ -250,18 +250,45 @@ var ServerBuilder = class {
250
250
  fn(this.app, this.container);
251
251
  return this;
252
252
  }
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
- }));
253
+ withSwagger(configure, opts, ...handlers) {
254
+ const config = this.swaggerRef.doc;
255
+ if (!config) throw new Error("withSwagger must be called before build()");
256
+ configure(new SwaggerBuilder(config));
257
+ const { path: path2, uiOpts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle } = opts;
258
+ const uiOptsWithDefaults = {
259
+ customSiteTitle: config.info?.title,
260
+ ...uiOpts
261
+ };
262
+ this.app.use(
263
+ path2,
264
+ ...handlers,
265
+ import_swagger_ui_express.default.serve,
266
+ import_swagger_ui_express.default.setup(
267
+ config,
268
+ uiOptsWithDefaults,
269
+ options,
270
+ customCss,
271
+ customfavIcon,
272
+ swaggerUrl,
273
+ customSiteTitle
274
+ )
275
+ );
257
276
  return this;
258
277
  }
259
278
  };
260
- function buildExpressive(container, swaggerDoc) {
279
+ function buildExpressive(container) {
280
+ const swaggerRef = {
281
+ doc: {
282
+ openapi: "3.1.0",
283
+ info: {},
284
+ paths: {},
285
+ components: {}
286
+ }
287
+ };
261
288
  return {
262
289
  expressiveServer(configs) {
263
290
  const app = configs?.app ?? (0, import_express.default)();
264
- return new ServerBuilder(app, container);
291
+ return new ServerBuilder(app, container, swaggerRef);
265
292
  },
266
293
  expressiveRouter(configs) {
267
294
  const router = import_express.default.Router();
@@ -304,10 +331,12 @@ function buildExpressive(container, swaggerDoc) {
304
331
  } else {
305
332
  pathItem.parameters.push(...tryParsePathParameters(route));
306
333
  }
307
- if (!swaggerDoc.paths[route]) {
308
- swaggerDoc.paths[route] = {};
334
+ if (swaggerRef.doc) {
335
+ if (!swaggerRef.doc.paths[route]) {
336
+ swaggerRef.doc.paths[route] = {};
337
+ }
338
+ swaggerRef.doc.paths[route][context.method] = pathItem;
309
339
  }
310
- swaggerDoc.paths[route][context.method] = pathItem;
311
340
  return router;
312
341
  }
313
342
  };
@@ -545,17 +574,9 @@ var ApiResponse = class {
545
574
 
546
575
  // src/index.ts
547
576
  function bootstrap(container) {
548
- const swaggerDoc = {
549
- openapi: "3.1.0",
550
- // TODO
551
- info: {},
552
- paths: {},
553
- components: {}
554
- };
555
577
  return {
556
- ...buildExpressive(container, swaggerDoc),
578
+ ...buildExpressive(container),
557
579
  ...buildMiddleware(container),
558
- swaggerBuilder: () => new SwaggerBuilder(swaggerDoc),
559
580
  silently: async (fn) => {
560
581
  try {
561
582
  await fn();
package/dist/index.mjs CHANGED
@@ -30,9 +30,6 @@ var SwaggerBuilder = class {
30
30
  this.swaggerDoc.security = globalAuthMethods;
31
31
  return this;
32
32
  }
33
- build() {
34
- return this.swaggerDoc;
35
- }
36
33
  };
37
34
  var securitySchemes = {
38
35
  BasicAuth: () => ({
@@ -150,11 +147,13 @@ var SWG = {
150
147
 
151
148
  // src/expressive.ts
152
149
  var ServerBuilder = class {
153
- constructor(app, container) {
150
+ constructor(app, container, swaggerRef) {
154
151
  this.app = app;
155
152
  this.container = container;
153
+ this.swaggerRef = swaggerRef;
156
154
  }
157
155
  build() {
156
+ this.swaggerRef.doc = null;
158
157
  return this.app;
159
158
  }
160
159
  withHelmet(options) {
@@ -172,6 +171,7 @@ var ServerBuilder = class {
172
171
  withMorgan(format, options) {
173
172
  this.app.use(morgan(
174
173
  format ?? ":req[x-real-ip] :method :url :status :res[content-length] - :response-time ms",
174
+ // TODO: ip or x-real-ip; also, default to json?
175
175
  options ?? { stream: { write: (message) => {
176
176
  this.container.logger.info(message.trim());
177
177
  } } }
@@ -189,18 +189,45 @@ var ServerBuilder = class {
189
189
  fn(this.app, this.container);
190
190
  return this;
191
191
  }
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
- }));
192
+ withSwagger(configure, opts, ...handlers) {
193
+ const config = this.swaggerRef.doc;
194
+ if (!config) throw new Error("withSwagger must be called before build()");
195
+ configure(new SwaggerBuilder(config));
196
+ const { path: path2, uiOpts, options, customCss, customfavIcon, swaggerUrl, customSiteTitle } = opts;
197
+ const uiOptsWithDefaults = {
198
+ customSiteTitle: config.info?.title,
199
+ ...uiOpts
200
+ };
201
+ this.app.use(
202
+ path2,
203
+ ...handlers,
204
+ swaggerUi.serve,
205
+ swaggerUi.setup(
206
+ config,
207
+ uiOptsWithDefaults,
208
+ options,
209
+ customCss,
210
+ customfavIcon,
211
+ swaggerUrl,
212
+ customSiteTitle
213
+ )
214
+ );
196
215
  return this;
197
216
  }
198
217
  };
199
- function buildExpressive(container, swaggerDoc) {
218
+ function buildExpressive(container) {
219
+ const swaggerRef = {
220
+ doc: {
221
+ openapi: "3.1.0",
222
+ info: {},
223
+ paths: {},
224
+ components: {}
225
+ }
226
+ };
200
227
  return {
201
228
  expressiveServer(configs) {
202
229
  const app = configs?.app ?? express();
203
- return new ServerBuilder(app, container);
230
+ return new ServerBuilder(app, container, swaggerRef);
204
231
  },
205
232
  expressiveRouter(configs) {
206
233
  const router = express.Router();
@@ -243,10 +270,12 @@ function buildExpressive(container, swaggerDoc) {
243
270
  } else {
244
271
  pathItem.parameters.push(...tryParsePathParameters(route));
245
272
  }
246
- if (!swaggerDoc.paths[route]) {
247
- swaggerDoc.paths[route] = {};
273
+ if (swaggerRef.doc) {
274
+ if (!swaggerRef.doc.paths[route]) {
275
+ swaggerRef.doc.paths[route] = {};
276
+ }
277
+ swaggerRef.doc.paths[route][context.method] = pathItem;
248
278
  }
249
- swaggerDoc.paths[route][context.method] = pathItem;
250
279
  return router;
251
280
  }
252
281
  };
@@ -484,17 +513,9 @@ var ApiResponse = class {
484
513
 
485
514
  // src/index.ts
486
515
  function bootstrap(container) {
487
- const swaggerDoc = {
488
- openapi: "3.1.0",
489
- // TODO
490
- info: {},
491
- paths: {},
492
- components: {}
493
- };
494
516
  return {
495
- ...buildExpressive(container, swaggerDoc),
517
+ ...buildExpressive(container),
496
518
  ...buildMiddleware(container),
497
- swaggerBuilder: () => new SwaggerBuilder(swaggerDoc),
498
519
  silently: async (fn) => {
499
520
  try {
500
521
  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.9.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"