@extk/expressive 0.10.0 → 0.11.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/README.md CHANGED
@@ -224,6 +224,17 @@ addRoute({
224
224
  }, handler);
225
225
  ```
226
226
 
227
+ Pass `enabled: false` to skip swagger registration conditionally without breaking the chain:
228
+
229
+ ```ts
230
+ .withSwagger(
231
+ b => b.withInfo({ title: 'My API', version: '1.0' }),
232
+ { path: '/api-docs', enabled: isDev() },
233
+ )
234
+ ```
235
+
236
+ When `enabled` is `false`, `withSwagger` is a no-op and the chain continues normally. Omitting `enabled` (or passing `true`) keeps existing behavior.
237
+
227
238
  Configure security schemes via the `configure` callback in `withSwagger`:
228
239
 
229
240
  ```ts
package/dist/index.d.mts CHANGED
@@ -225,10 +225,12 @@ declare const SWG: {
225
225
  };
226
226
 
227
227
  type SwaggerRef = {
228
- doc: SwaggerConfig | null;
228
+ doc?: SwaggerConfig | null;
229
+ bootstrapOptions?: SwaggerBootstrapOpts | null;
229
230
  };
230
- type ExpressiveSwaggerOptions = {
231
- path: ExpressRoute;
231
+ type SwaggerBootstrapOpts = {
232
+ enabled?: boolean;
233
+ configure?: (builder: SwaggerBuilder) => void;
232
234
  uiOpts?: SwaggerUiOptions;
233
235
  options?: SwaggerOptions;
234
236
  customCss?: string;
@@ -236,6 +238,9 @@ type ExpressiveSwaggerOptions = {
236
238
  swaggerUrl?: string;
237
239
  customSiteTitle?: string;
238
240
  };
241
+ type BuildExpressiveOpts = {
242
+ swagger?: SwaggerBootstrapOpts;
243
+ };
239
244
  declare class ServerBuilder {
240
245
  private app;
241
246
  private container;
@@ -249,7 +254,7 @@ declare class ServerBuilder {
249
254
  * Helper function for fluent design
250
255
  */
251
256
  with(fn: (app: express__default.Express, container: Container) => void): this;
252
- withSwagger(configure: (builder: SwaggerBuilder) => void, opts: ExpressiveSwaggerOptions, ...handlers: ExpressHandler[]): this;
257
+ withSwagger(path?: string, ...handlers: ExpressHandler[]): this;
253
258
  }
254
259
 
255
260
  declare class ApiError extends Error {
@@ -323,7 +328,7 @@ declare class ApiResponse<T = undefined> {
323
328
  constructor(result?: T);
324
329
  }
325
330
 
326
- declare function bootstrap(container: Container): {
331
+ declare function bootstrap(container: Container, opts?: BuildExpressiveOpts): {
327
332
  silently: (fn: () => Promise<void> | void) => Promise<void>;
328
333
  getGlobalNotFoundMiddleware: (content?: string) => (_req: express.Request, res: express.Response, _next: express.NextFunction) => void;
329
334
  getApiNotFoundMiddleware: () => (req: express.Request, res: express.Response, _next: express.NextFunction) => void;
package/dist/index.d.ts CHANGED
@@ -225,10 +225,12 @@ declare const SWG: {
225
225
  };
226
226
 
227
227
  type SwaggerRef = {
228
- doc: SwaggerConfig | null;
228
+ doc?: SwaggerConfig | null;
229
+ bootstrapOptions?: SwaggerBootstrapOpts | null;
229
230
  };
230
- type ExpressiveSwaggerOptions = {
231
- path: ExpressRoute;
231
+ type SwaggerBootstrapOpts = {
232
+ enabled?: boolean;
233
+ configure?: (builder: SwaggerBuilder) => void;
232
234
  uiOpts?: SwaggerUiOptions;
233
235
  options?: SwaggerOptions;
234
236
  customCss?: string;
@@ -236,6 +238,9 @@ type ExpressiveSwaggerOptions = {
236
238
  swaggerUrl?: string;
237
239
  customSiteTitle?: string;
238
240
  };
241
+ type BuildExpressiveOpts = {
242
+ swagger?: SwaggerBootstrapOpts;
243
+ };
239
244
  declare class ServerBuilder {
240
245
  private app;
241
246
  private container;
@@ -249,7 +254,7 @@ declare class ServerBuilder {
249
254
  * Helper function for fluent design
250
255
  */
251
256
  with(fn: (app: express__default.Express, container: Container) => void): this;
252
- withSwagger(configure: (builder: SwaggerBuilder) => void, opts: ExpressiveSwaggerOptions, ...handlers: ExpressHandler[]): this;
257
+ withSwagger(path?: string, ...handlers: ExpressHandler[]): this;
253
258
  }
254
259
 
255
260
  declare class ApiError extends Error {
@@ -323,7 +328,7 @@ declare class ApiResponse<T = undefined> {
323
328
  constructor(result?: T);
324
329
  }
325
330
 
326
- declare function bootstrap(container: Container): {
331
+ declare function bootstrap(container: Container, opts?: BuildExpressiveOpts): {
327
332
  silently: (fn: () => Promise<void> | void) => Promise<void>;
328
333
  getGlobalNotFoundMiddleware: (content?: string) => (_req: express.Request, res: express.Response, _next: express.NextFunction) => void;
329
334
  getApiNotFoundMiddleware: () => (req: express.Request, res: express.Response, _next: express.NextFunction) => void;
package/dist/index.js CHANGED
@@ -213,7 +213,10 @@ var ServerBuilder = class {
213
213
  this.swaggerRef = swaggerRef;
214
214
  }
215
215
  build() {
216
- this.swaggerRef.doc = null;
216
+ if (this.swaggerRef) {
217
+ this.swaggerRef.doc = null;
218
+ this.swaggerRef.bootstrapOptions = null;
219
+ }
217
220
  return this.app;
218
221
  }
219
222
  withHelmet(options) {
@@ -240,22 +243,30 @@ var ServerBuilder = class {
240
243
  fn(this.app, this.container);
241
244
  return this;
242
245
  }
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
- };
246
+ withSwagger(path2 = "/api-docs", ...handlers) {
247
+ if (!this.swaggerRef || !this.swaggerRef.doc) {
248
+ return this;
249
+ }
250
+ const doc = this.swaggerRef.doc;
251
+ const {
252
+ configure,
253
+ uiOpts,
254
+ options,
255
+ customCss,
256
+ customfavIcon,
257
+ swaggerUrl,
258
+ customSiteTitle
259
+ } = this.swaggerRef.bootstrapOptions ?? {};
260
+ if (configure) {
261
+ configure(new SwaggerBuilder(doc));
262
+ }
252
263
  this.app.use(
253
264
  path2,
254
265
  ...handlers,
255
266
  import_swagger_ui_express.default.serve,
256
267
  import_swagger_ui_express.default.setup(
257
- config,
258
- uiOptsWithDefaults,
268
+ doc,
269
+ { customSiteTitle: doc.info?.title, ...uiOpts },
259
270
  options,
260
271
  customCss,
261
272
  customfavIcon,
@@ -266,14 +277,11 @@ var ServerBuilder = class {
266
277
  return this;
267
278
  }
268
279
  };
269
- function buildExpressive(container) {
270
- const swaggerRef = {
271
- doc: {
272
- openapi: "3.1.0",
273
- info: {},
274
- paths: {},
275
- components: {}
276
- }
280
+ function buildExpressive(container, opts) {
281
+ const swaggerBootstrapOpts = opts?.swagger;
282
+ const swaggerRef = !swaggerBootstrapOpts || swaggerBootstrapOpts.enabled === false ? {} : {
283
+ doc: { openapi: "3.1.0", info: {}, paths: {}, components: {} },
284
+ bootstrapOptions: swaggerBootstrapOpts
277
285
  };
278
286
  return {
279
287
  expressiveServer(configs) {
@@ -286,42 +294,42 @@ function buildExpressive(container) {
286
294
  router,
287
295
  addRoute(context, ...handlers) {
288
296
  router[context.method](context.path, ...handlers);
289
- const {
290
- pathOverride,
291
- pathParameters,
292
- headerParameters,
293
- queryParameters,
294
- ...pathItemConfig
295
- } = context.oapi || {};
296
- const route = pathOverride ?? convertExpressPath(context.path);
297
- const pathItem = {
298
- // -- defaults --
299
- responses: {
300
- // has to be defined or else responses are not documented... ¯\_(ツ)_/¯
301
- "200": { description: "OK" },
302
- "201": { description: "Created" },
303
- "204": { description: "No Content" },
304
- "400": { description: "Bad Request" },
305
- "401": { description: "User Unauthorized" },
306
- "403": { description: "Forbidden" },
307
- "500": { description: "Internal Server Error" }
308
- },
309
- // -- group defaults --
310
- ...configs?.oapi || {},
311
- // -- overrides --
312
- ...pathItemConfig || {},
313
- parameters: [
314
- // ...(contract.pathParameters || []),
315
- ...headerParameters || [],
316
- ...queryParameters || []
317
- ]
318
- };
319
- if (pathParameters?.length) {
320
- pathItem.parameters.push(...pathParameters);
321
- } else {
322
- pathItem.parameters.push(...tryParsePathParameters(route));
323
- }
324
- if (swaggerRef.doc) {
297
+ if (swaggerRef?.doc) {
298
+ const {
299
+ pathOverride,
300
+ pathParameters,
301
+ headerParameters,
302
+ queryParameters,
303
+ ...pathItemConfig
304
+ } = context.oapi || {};
305
+ const route = pathOverride ?? convertExpressPath(context.path);
306
+ const pathItem = {
307
+ // -- defaults --
308
+ responses: {
309
+ // has to be defined or else responses are not documented... ¯\_(ツ)_/¯
310
+ "200": { description: "OK" },
311
+ "201": { description: "Created" },
312
+ "204": { description: "No Content" },
313
+ "400": { description: "Bad Request" },
314
+ "401": { description: "User Unauthorized" },
315
+ "403": { description: "Forbidden" },
316
+ "500": { description: "Internal Server Error" }
317
+ },
318
+ // -- group defaults --
319
+ ...configs?.oapi || {},
320
+ // -- overrides --
321
+ ...pathItemConfig || {},
322
+ parameters: [
323
+ // ...(contract.pathParameters || []),
324
+ ...headerParameters || [],
325
+ ...queryParameters || []
326
+ ]
327
+ };
328
+ if (pathParameters?.length) {
329
+ pathItem.parameters.push(...pathParameters);
330
+ } else {
331
+ pathItem.parameters.push(...tryParsePathParameters(route));
332
+ }
325
333
  if (!swaggerRef.doc.paths[route]) {
326
334
  swaggerRef.doc.paths[route] = {};
327
335
  }
@@ -563,9 +571,9 @@ var ApiResponse = class {
563
571
  };
564
572
 
565
573
  // src/index.ts
566
- function bootstrap(container) {
574
+ function bootstrap(container, opts) {
567
575
  return {
568
- ...buildExpressive(container),
576
+ ...buildExpressive(container, opts),
569
577
  ...buildMiddleware(container),
570
578
  silently: async (fn) => {
571
579
  try {
package/dist/index.mjs CHANGED
@@ -152,7 +152,10 @@ var ServerBuilder = class {
152
152
  this.swaggerRef = swaggerRef;
153
153
  }
154
154
  build() {
155
- this.swaggerRef.doc = null;
155
+ if (this.swaggerRef) {
156
+ this.swaggerRef.doc = null;
157
+ this.swaggerRef.bootstrapOptions = null;
158
+ }
156
159
  return this.app;
157
160
  }
158
161
  withHelmet(options) {
@@ -179,22 +182,30 @@ var ServerBuilder = class {
179
182
  fn(this.app, this.container);
180
183
  return this;
181
184
  }
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
- };
185
+ withSwagger(path2 = "/api-docs", ...handlers) {
186
+ if (!this.swaggerRef || !this.swaggerRef.doc) {
187
+ return this;
188
+ }
189
+ const doc = this.swaggerRef.doc;
190
+ const {
191
+ configure,
192
+ uiOpts,
193
+ options,
194
+ customCss,
195
+ customfavIcon,
196
+ swaggerUrl,
197
+ customSiteTitle
198
+ } = this.swaggerRef.bootstrapOptions ?? {};
199
+ if (configure) {
200
+ configure(new SwaggerBuilder(doc));
201
+ }
191
202
  this.app.use(
192
203
  path2,
193
204
  ...handlers,
194
205
  swaggerUi.serve,
195
206
  swaggerUi.setup(
196
- config,
197
- uiOptsWithDefaults,
207
+ doc,
208
+ { customSiteTitle: doc.info?.title, ...uiOpts },
198
209
  options,
199
210
  customCss,
200
211
  customfavIcon,
@@ -205,14 +216,11 @@ var ServerBuilder = class {
205
216
  return this;
206
217
  }
207
218
  };
208
- function buildExpressive(container) {
209
- const swaggerRef = {
210
- doc: {
211
- openapi: "3.1.0",
212
- info: {},
213
- paths: {},
214
- components: {}
215
- }
219
+ function buildExpressive(container, opts) {
220
+ const swaggerBootstrapOpts = opts?.swagger;
221
+ const swaggerRef = !swaggerBootstrapOpts || swaggerBootstrapOpts.enabled === false ? {} : {
222
+ doc: { openapi: "3.1.0", info: {}, paths: {}, components: {} },
223
+ bootstrapOptions: swaggerBootstrapOpts
216
224
  };
217
225
  return {
218
226
  expressiveServer(configs) {
@@ -225,42 +233,42 @@ function buildExpressive(container) {
225
233
  router,
226
234
  addRoute(context, ...handlers) {
227
235
  router[context.method](context.path, ...handlers);
228
- const {
229
- pathOverride,
230
- pathParameters,
231
- headerParameters,
232
- queryParameters,
233
- ...pathItemConfig
234
- } = context.oapi || {};
235
- const route = pathOverride ?? convertExpressPath(context.path);
236
- const pathItem = {
237
- // -- defaults --
238
- responses: {
239
- // has to be defined or else responses are not documented... ¯\_(ツ)_/¯
240
- "200": { description: "OK" },
241
- "201": { description: "Created" },
242
- "204": { description: "No Content" },
243
- "400": { description: "Bad Request" },
244
- "401": { description: "User Unauthorized" },
245
- "403": { description: "Forbidden" },
246
- "500": { description: "Internal Server Error" }
247
- },
248
- // -- group defaults --
249
- ...configs?.oapi || {},
250
- // -- overrides --
251
- ...pathItemConfig || {},
252
- parameters: [
253
- // ...(contract.pathParameters || []),
254
- ...headerParameters || [],
255
- ...queryParameters || []
256
- ]
257
- };
258
- if (pathParameters?.length) {
259
- pathItem.parameters.push(...pathParameters);
260
- } else {
261
- pathItem.parameters.push(...tryParsePathParameters(route));
262
- }
263
- if (swaggerRef.doc) {
236
+ if (swaggerRef?.doc) {
237
+ const {
238
+ pathOverride,
239
+ pathParameters,
240
+ headerParameters,
241
+ queryParameters,
242
+ ...pathItemConfig
243
+ } = context.oapi || {};
244
+ const route = pathOverride ?? convertExpressPath(context.path);
245
+ const pathItem = {
246
+ // -- defaults --
247
+ responses: {
248
+ // has to be defined or else responses are not documented... ¯\_(ツ)_/¯
249
+ "200": { description: "OK" },
250
+ "201": { description: "Created" },
251
+ "204": { description: "No Content" },
252
+ "400": { description: "Bad Request" },
253
+ "401": { description: "User Unauthorized" },
254
+ "403": { description: "Forbidden" },
255
+ "500": { description: "Internal Server Error" }
256
+ },
257
+ // -- group defaults --
258
+ ...configs?.oapi || {},
259
+ // -- overrides --
260
+ ...pathItemConfig || {},
261
+ parameters: [
262
+ // ...(contract.pathParameters || []),
263
+ ...headerParameters || [],
264
+ ...queryParameters || []
265
+ ]
266
+ };
267
+ if (pathParameters?.length) {
268
+ pathItem.parameters.push(...pathParameters);
269
+ } else {
270
+ pathItem.parameters.push(...tryParsePathParameters(route));
271
+ }
264
272
  if (!swaggerRef.doc.paths[route]) {
265
273
  swaggerRef.doc.paths[route] = {};
266
274
  }
@@ -502,9 +510,9 @@ var ApiResponse = class {
502
510
  };
503
511
 
504
512
  // src/index.ts
505
- function bootstrap(container) {
513
+ function bootstrap(container, opts) {
506
514
  return {
507
- ...buildExpressive(container),
515
+ ...buildExpressive(container, opts),
508
516
  ...buildMiddleware(container),
509
517
  silently: async (fn) => {
510
518
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@extk/expressive",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "type": "commonjs",
5
5
  "publishConfig": {
6
6
  "access": "public"