express-zod-api 18.5.2 → 19.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,10 +1,10 @@
1
+ import * as zod from 'zod';
2
+ import { z, ZodError } from 'zod';
1
3
  import compression from 'compression';
2
4
  import express, { Request, Response, NextFunction, RequestHandler, IRouter } from 'express';
3
5
  import * as express_fileupload from 'express-fileupload';
4
6
  import express_fileupload__default from 'express-fileupload';
5
7
  import https, { ServerOptions } from 'node:https';
6
- import * as zod from 'zod';
7
- import { z, ZodError } from 'zod';
8
8
  import { HttpError } from 'http-errors';
9
9
  import { ListenOptions } from 'node:net';
10
10
  import * as qs from 'qs';
@@ -13,6 +13,37 @@ import http from 'node:http';
13
13
  import { OpenApiBuilder, SecuritySchemeType, SchemaObject, ReferenceObject, SecuritySchemeObject } from 'openapi3-ts/oas31';
14
14
  import ts from 'typescript';
15
15
 
16
+ declare const metaSymbol: unique symbol;
17
+ interface Metadata<T extends z.ZodTypeAny> {
18
+ examples: z.input<T>[];
19
+ /** @override ZodDefault::_def.defaultValue() in depictDefault */
20
+ defaultLabel?: string;
21
+ brand?: string | number | symbol;
22
+ }
23
+
24
+ /**
25
+ * @fileoverview Zod Runtime Plugin
26
+ * @see https://github.com/colinhacks/zod/blob/90efe7fa6135119224412c7081bd12ef0bccef26/plugin/effect/src/index.ts#L21-L31
27
+ * @desc This code modifies and extends zod's functionality immediately when importing express-zod-api
28
+ * @desc Enables .examples() on all schemas (ZodType)
29
+ * @desc Enables .label() on ZodDefault
30
+ * @desc Stores the argument supplied to .brand() on all schema (runtime distinguishable branded types)
31
+ * */
32
+
33
+ declare module "zod" {
34
+ interface ZodTypeDef {
35
+ [metaSymbol]?: Metadata<z.ZodTypeAny>;
36
+ }
37
+ interface ZodType {
38
+ /** @desc Add an example value (before any transformations, can be called multiple times) */
39
+ example(example: this["_input"]): this;
40
+ }
41
+ interface ZodDefault<T extends z.ZodTypeAny> {
42
+ /** @desc Change the default value in the generated Documentation to a label */
43
+ label(label: string): this;
44
+ }
45
+ }
46
+
16
47
  interface ApiResponse<S extends z.ZodTypeAny> {
17
48
  schema: S;
18
49
  /**
@@ -195,17 +226,51 @@ interface ExpressMiddlewareFeatures<R extends Request, S extends Response, OUT e
195
226
  transformer?: (err: Error) => HttpError | Error;
196
227
  }
197
228
 
229
+ declare const ezFileBrand: unique symbol;
230
+ declare const variants: {
231
+ buffer: () => z.ZodBranded<z.ZodType<Buffer, z.ZodTypeDef, Buffer>, typeof ezFileBrand>;
232
+ string: () => z.ZodBranded<z.ZodString, typeof ezFileBrand>;
233
+ binary: () => z.ZodBranded<z.ZodUnion<[z.ZodType<Buffer, z.ZodTypeDef, Buffer>, z.ZodString]>, typeof ezFileBrand>;
234
+ base64: () => z.ZodBranded<z.ZodString, typeof ezFileBrand>;
235
+ };
236
+ type Variants = typeof variants;
237
+ type Variant = keyof Variants;
238
+ declare function file(): ReturnType<Variants["string"]>;
239
+ declare function file<K extends Variant>(variant: K): ReturnType<Variants[K]>;
240
+
241
+ declare const ezRawBrand: unique symbol;
242
+ /** Shorthand for z.object({ raw: ez.file("buffer") }) */
243
+ declare const raw: <S extends z.ZodRawShape>(extra?: S) => z.ZodBranded<z.ZodObject<z.objectUtil.extendShape<{
244
+ raw: z.ZodBranded<z.ZodType<Buffer, z.ZodTypeDef, Buffer>, typeof ezFileBrand>;
245
+ }, S>, "strip", z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<z.objectUtil.extendShape<{
246
+ raw: z.ZodBranded<z.ZodType<Buffer, z.ZodTypeDef, Buffer>, typeof ezFileBrand>;
247
+ }, S>>, any> extends infer T ? { [k in keyof T]: z.objectUtil.addQuestionMarks<z.baseObjectOutputType<z.objectUtil.extendShape<{
248
+ raw: z.ZodBranded<z.ZodType<Buffer, z.ZodTypeDef, Buffer>, typeof ezFileBrand>;
249
+ }, S>>, any>[k]; } : never, z.baseObjectInputType<z.objectUtil.extendShape<{
250
+ raw: z.ZodBranded<z.ZodType<Buffer, z.ZodTypeDef, Buffer>, typeof ezFileBrand>;
251
+ }, S>> extends infer T_1 ? { [k_1 in keyof T_1]: z.baseObjectInputType<z.objectUtil.extendShape<{
252
+ raw: z.ZodBranded<z.ZodType<Buffer, z.ZodTypeDef, Buffer>, typeof ezFileBrand>;
253
+ }, S>>[k_1]; } : never>, typeof ezRawBrand>;
254
+ type RawSchema = ReturnType<typeof raw>;
255
+
198
256
  type Refined<T extends z.ZodTypeAny> = T extends z.ZodType<infer O> ? z.ZodEffects<T | Refined<T>, O, O> : never;
199
257
  /**
200
258
  * @desc The type allowed on the top level of Middlewares and Endpoints
201
259
  * @param U — only "strip" is allowed for Middlewares due to intersection issue (Zod) #600
202
260
  * */
203
- type IOSchema<U extends z.UnknownKeysParam = any> = z.ZodObject<any, U> | z.ZodUnion<[IOSchema<U>, ...IOSchema<U>[]]> | z.ZodIntersection<IOSchema<U>, IOSchema<U>> | z.ZodDiscriminatedUnion<string, z.ZodObject<any, U>[]> | Refined<z.ZodObject<any, U>>;
261
+ type IOSchema<U extends z.UnknownKeysParam = any> = z.ZodObject<any, U> | z.ZodUnion<[IOSchema<U>, ...IOSchema<U>[]]> | z.ZodIntersection<IOSchema<U>, IOSchema<U>> | z.ZodDiscriminatedUnion<string, z.ZodObject<any, U>[]> | Refined<z.ZodObject<any, U>> | RawSchema;
204
262
  type ProbableIntersection<A extends IOSchema<"strip"> | null, B extends IOSchema> = A extends null ? B : A extends IOSchema<"strip"> ? z.ZodIntersection<A, B> : never;
205
263
 
206
264
  declare const methods: ("get" | "post" | "put" | "delete" | "patch")[];
207
265
  type Method = (typeof methods)[number];
208
266
 
267
+ declare const contentTypes: {
268
+ json: string;
269
+ upload: string;
270
+ raw: string;
271
+ };
272
+ type ContentType = keyof typeof contentTypes;
273
+
209
274
  interface ResultHandlerParams<RES> {
210
275
  /** null in case of failure to parse or to find the matching endpoint (error: not found) */
211
276
  input: FlatObject | null;
@@ -278,18 +343,19 @@ declare abstract class AbstractEndpoint {
278
343
  response: Response;
279
344
  logger: AbstractLogger;
280
345
  config: CommonConfig;
281
- siblingMethods?: Method[];
346
+ siblingMethods?: ReadonlyArray<Method>;
282
347
  }): Promise<void>;
283
348
  abstract getDescription(variant: DescriptionVariant): string | undefined;
284
- abstract getMethods(): Method[];
349
+ abstract getMethods(): ReadonlyArray<Method>;
285
350
  abstract getSchema(variant: IOVariant): IOSchema;
286
351
  abstract getSchema(variant: ResponseVariant): z.ZodTypeAny;
287
- abstract getMimeTypes(variant: MimeVariant): string[];
288
- abstract getResponses(variant: ResponseVariant): NormalizedResponse[];
352
+ abstract getMimeTypes(variant: MimeVariant): ReadonlyArray<string>;
353
+ abstract getResponses(variant: ResponseVariant): ReadonlyArray<NormalizedResponse>;
289
354
  abstract getSecurity(): LogicalContainer<Security>;
290
- abstract getScopes(): string[];
291
- abstract getTags(): string[];
355
+ abstract getScopes(): ReadonlyArray<string>;
356
+ abstract getTags(): ReadonlyArray<string>;
292
357
  abstract getOperationId(method: Method): string | undefined;
358
+ abstract getRequestType(): ContentType;
293
359
  }
294
360
  declare class Endpoint<IN extends IOSchema, OUT extends IOSchema, OPT extends FlatObject, SCO extends string, TAG extends string> extends AbstractEndpoint {
295
361
  #private;
@@ -307,15 +373,16 @@ declare class Endpoint<IN extends IOSchema, OUT extends IOSchema, OPT extends Fl
307
373
  tags?: TAG[];
308
374
  });
309
375
  getDescription(variant: DescriptionVariant): string | undefined;
310
- getMethods(): Method[];
376
+ getMethods(): readonly ("get" | "post" | "put" | "delete" | "patch")[];
311
377
  getSchema(variant: "input"): IN;
312
378
  getSchema(variant: "output"): OUT;
313
379
  getSchema(variant: ResponseVariant): z.ZodTypeAny;
314
- getMimeTypes(variant: MimeVariant): string[];
315
- getResponses(variant: ResponseVariant): Required<Pick<ApiResponse<z.ZodTypeAny>, "schema" | "statusCodes" | "mimeTypes">>[];
380
+ getMimeTypes(variant: MimeVariant): readonly string[];
381
+ getRequestType(): "raw" | "json" | "upload";
382
+ getResponses(variant: ResponseVariant): readonly Required<Pick<ApiResponse<z.ZodTypeAny>, "schema" | "statusCodes" | "mimeTypes">>[];
316
383
  getSecurity(): LogicalContainer<Security>;
317
- getScopes(): SCO[];
318
- getTags(): TAG[];
384
+ getScopes(): readonly SCO[];
385
+ getTags(): readonly TAG[];
319
386
  getOperationId(method: Method): string | undefined;
320
387
  execute({ request, response, logger, config, siblingMethods, }: {
321
388
  request: Request;
@@ -385,6 +452,10 @@ interface CommonConfig<TAG extends string = string> {
385
452
  */
386
453
  tags?: TagsConfig<TAG>;
387
454
  }
455
+ type BeforeUpload = (params: {
456
+ request: Request;
457
+ logger: AbstractLogger;
458
+ }) => void | Promise<void>;
388
459
  type UploadOptions = Pick<express_fileupload__default.Options, "createParentPath" | "uriDecodeFileNames" | "safeFileNames" | "preserveExtension" | "useTempFiles" | "tempFileDir" | "debug" | "uploadTimeout" | "limits"> & {
389
460
  /**
390
461
  * @desc The error to throw when the file exceeds the configured fileSize limit (handled by errorHandler).
@@ -394,15 +465,14 @@ type UploadOptions = Pick<express_fileupload__default.Options, "createParentPath
394
465
  * */
395
466
  limitError?: Error;
396
467
  /**
397
- * @desc A code to execute before connecting the upload middleware.
398
- * @desc It can be used to connect a middleware that restricts the ability to upload.
468
+ * @desc A handler to execute before uploading it can be used for restrictions by throwing an error.
399
469
  * @default undefined
400
- * @example ({ app }) => { app.use( ... ); }
470
+ * @example ({ request }) => { throw createHttpError(403, "Not authorized"); }
401
471
  * */
402
- beforeUpload?: AppExtension;
472
+ beforeUpload?: BeforeUpload;
403
473
  };
404
474
  type CompressionOptions = Pick<compression.CompressionOptions, "threshold" | "level" | "strategy" | "chunkSize" | "memLevel">;
405
- type AppExtension = (params: {
475
+ type BeforeRouting = (params: {
406
476
  app: IRouter;
407
477
  logger: AbstractLogger;
408
478
  }) => void | Promise<void>;
@@ -419,21 +489,19 @@ interface ServerConfig<TAG extends string = string> extends CommonConfig<TAG> {
419
489
  jsonParser?: RequestHandler;
420
490
  /**
421
491
  * @desc Enable or configure uploads handling.
422
- * @default false
492
+ * @default undefined
423
493
  * @requires express-fileupload
424
494
  * */
425
495
  upload?: boolean | UploadOptions;
426
496
  /**
427
497
  * @desc Enable or configure response compression.
428
- * @default false
498
+ * @default undefined
429
499
  * @requires compression
430
500
  */
431
501
  compression?: boolean | CompressionOptions;
432
502
  /**
433
- * @desc Enables parsing certain request payloads into raw Buffers (application/octet-stream by default)
434
- * @desc When enabled, use ez.raw() as input schema to get input.raw in Endpoint's handler
435
- * @default undefined
436
- * @example express.raw()
503
+ * @desc Custom raw parser (assigns Buffer to request body)
504
+ * @default express.raw()
437
505
  * @link https://expressjs.com/en/4x/api.html#express.raw
438
506
  * */
439
507
  rawParser?: RequestHandler;
@@ -444,7 +512,7 @@ interface ServerConfig<TAG extends string = string> extends CommonConfig<TAG> {
444
512
  * @default undefined
445
513
  * @example ({ app }) => { app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); }
446
514
  * */
447
- beforeRouting?: AppExtension;
515
+ beforeRouting?: BeforeRouting;
448
516
  };
449
517
  /** @desc Enables HTTPS server as well. */
450
518
  https?: {
@@ -506,9 +574,9 @@ declare const defaultEndpointsFactory: EndpointsFactory<null, {}, string, string
506
574
  declare const arrayEndpointsFactory: EndpointsFactory<null, {}, string, string>;
507
575
 
508
576
  declare class DependsOnMethod {
509
- readonly pairs: [Method, AbstractEndpoint][];
577
+ readonly pairs: ReadonlyArray<[Method, AbstractEndpoint]>;
510
578
  readonly firstEndpoint: AbstractEndpoint | undefined;
511
- readonly siblingMethods: Method[];
579
+ readonly siblingMethods: ReadonlyArray<Method>;
512
580
  constructor(endpoints: Partial<Record<Method, AbstractEndpoint>>);
513
581
  }
514
582
 
@@ -572,40 +640,29 @@ declare class Documentation extends OpenApiBuilder {
572
640
  constructor({ routing, config, title, version, serverUrl, descriptions, hasSummaryFromDescription, composition, serializer, }: DocumentationParams);
573
641
  }
574
642
 
575
- declare const ezRawKind = "Raw";
643
+ declare const ezDateInBrand: unique symbol;
576
644
 
577
- declare const ezDateInKind = "DateIn";
578
-
579
- declare const ezDateOutKind = "DateOut";
580
-
581
- declare const ezFileKind = "File";
582
- declare const variants: {
583
- buffer: () => z.ZodType<Buffer, z.ZodTypeDef, Buffer>;
584
- string: () => z.ZodString;
585
- binary: () => z.ZodUnion<[z.ZodType<Buffer, z.ZodTypeDef, Buffer>, z.ZodString]>;
586
- base64: () => z.ZodString;
587
- };
588
- type Variants = typeof variants;
589
- type Variant = keyof Variants;
590
- declare function file(): ReturnType<Variants["string"]>;
591
- declare function file<K extends Variant>(variant: K): ReturnType<Variants[K]>;
645
+ declare const ezDateOutBrand: unique symbol;
592
646
 
593
- declare const ezUploadKind = "Upload";
647
+ declare const ezUploadBrand: unique symbol;
594
648
 
595
649
  declare const ez: {
596
- dateIn: () => zod.ZodPipeline<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodString, zod.ZodString]>, Date, string>, zod.ZodEffects<zod.ZodDate, Date, Date>> | zod.ZodPipeline<zod.ZodEffects<zod.ZodString, Date, string>, zod.ZodEffects<zod.ZodDate, Date, Date>>;
597
- dateOut: () => zod.ZodEffects<zod.ZodEffects<zod.ZodDate, Date, Date>, string, Date>;
650
+ dateIn: () => zod.ZodBranded<zod.ZodPipeline<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodString, zod.ZodString]>, Date, string>, zod.ZodEffects<zod.ZodDate, Date, Date>>, typeof ezDateInBrand>;
651
+ dateOut: () => zod.ZodBranded<zod.ZodEffects<zod.ZodEffects<zod.ZodDate, Date, Date>, string, Date>, typeof ezDateOutBrand>;
598
652
  file: typeof file;
599
- upload: () => zod.ZodType<express_fileupload.UploadedFile, zod.ZodTypeDef, express_fileupload.UploadedFile>;
600
- raw: () => zod.ZodObject<{
601
- raw: zod.ZodType<Buffer, zod.ZodTypeDef, Buffer>;
602
- }, "strip", zod.ZodTypeAny, {
603
- raw: Buffer;
604
- }, {
605
- raw: Buffer;
606
- }>;
653
+ upload: () => zod.ZodBranded<zod.ZodType<express_fileupload.UploadedFile, zod.ZodTypeDef, express_fileupload.UploadedFile>, typeof ezUploadBrand>;
654
+ raw: <S extends zod.ZodRawShape>(extra?: S) => zod.ZodBranded<zod.ZodObject<zod.objectUtil.extendShape<{
655
+ raw: zod.ZodBranded<zod.ZodType<Buffer, zod.ZodTypeDef, Buffer>, typeof ezFileBrand>;
656
+ }, S>, "strip", zod.ZodTypeAny, zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<zod.objectUtil.extendShape<{
657
+ raw: zod.ZodBranded<zod.ZodType<Buffer, zod.ZodTypeDef, Buffer>, typeof ezFileBrand>;
658
+ }, S>>, any> extends infer T ? { [k in keyof T]: zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<zod.objectUtil.extendShape<{
659
+ raw: zod.ZodBranded<zod.ZodType<Buffer, zod.ZodTypeDef, Buffer>, typeof ezFileBrand>;
660
+ }, S>>, any>[k]; } : never, zod.baseObjectInputType<zod.objectUtil.extendShape<{
661
+ raw: zod.ZodBranded<zod.ZodType<Buffer, zod.ZodTypeDef, Buffer>, typeof ezFileBrand>;
662
+ }, S>> extends infer T_1 ? { [k_1 in keyof T_1]: zod.baseObjectInputType<zod.objectUtil.extendShape<{
663
+ raw: zod.ZodBranded<zod.ZodType<Buffer, zod.ZodTypeDef, Buffer>, typeof ezFileBrand>;
664
+ }, S>>[k_1]; } : never>, typeof ezRawBrand>;
607
665
  };
608
- type ProprietaryKind = typeof ezFileKind | typeof ezDateInKind | typeof ezDateOutKind | typeof ezUploadKind | typeof ezRawKind;
609
666
 
610
667
  interface OpenAPIContext extends FlatObject {
611
668
  isResponse: boolean;
@@ -650,31 +707,11 @@ declare class MissingPeerError extends Error {
650
707
  constructor(module: string | string[]);
651
708
  }
652
709
 
653
- declare const metaSymbol: unique symbol;
654
- interface Metadata<T extends z.ZodTypeAny> {
655
- kind?: ProprietaryKind;
656
- examples: z.input<T>[];
657
- /** @override ZodDefault::_def.defaultValue() in depictDefault */
658
- defaultLabel?: string;
659
- }
660
- declare module "zod" {
661
- interface ZodTypeDef {
662
- [metaSymbol]?: Metadata<z.ZodTypeAny>;
663
- }
664
- interface ZodType {
665
- /** @desc Add an example value (before any transformations, can be called multiple times) */
666
- example(example: this["_input"]): this;
667
- }
668
- interface ZodDefault<T extends z.ZodTypeAny> {
669
- /** @desc Change the default value in the generated Documentation to a label */
670
- label(label: string): this;
671
- }
672
- }
673
- /**
674
- * @deprecated no longer required
675
- * @todo remove in v19
676
- * */
677
- declare const withMeta: <T extends z.ZodTypeAny>(schema: T) => T;
710
+ type LocalResponse = Response<unknown, {
711
+ [metaSymbol]?: {
712
+ logger: AbstractLogger;
713
+ };
714
+ }>;
678
715
 
679
716
  /**
680
717
  * @desc Using module augmentation approach you can set the Mock type of your actual testing framework.
@@ -724,7 +761,8 @@ declare const testEndpoint: <LOG extends Record<string, any>, REQ extends Record
724
761
  writableEnded: boolean;
725
762
  statusCode: number;
726
763
  statusMessage: string;
727
- } & Record<"status" | "set" | "header" | "end" | "setHeader" | "json" | "send", MockOverrides> & RES;
764
+ locals: LocalResponse["locals"];
765
+ } & Record<"status" | "set" | "header" | "json" | "end" | "setHeader" | "send", MockOverrides> & RES;
728
766
  loggerMock: Record<"error" | "info" | "debug" | "warn", MockOverrides> & LOG;
729
767
  }>;
730
768
 
@@ -782,7 +820,7 @@ declare class Integration {
782
820
  path: string;
783
821
  }, Partial<Record<IOKind, string>> & {
784
822
  isJson: boolean;
785
- tags: string[];
823
+ tags: ReadonlyArray<string>;
786
824
  }>;
787
825
  protected paths: string[];
788
826
  protected aliases: Map<string, ts.TypeAliasDeclaration>;
@@ -828,4 +866,4 @@ declare class Integration {
828
866
  printFormatted({ printerOptions, format: userDefined, }?: FormattedPrintingOptions): Promise<string>;
829
867
  }
830
868
 
831
- export { AbstractEndpoint, type ApiResponse, type AppConfig, type BasicSecurity, type BearerSecurity, type CommonConfig, type CookieSecurity, type CustomHeaderSecurity, DependsOnMethod, Documentation, DocumentationError, EndpointsFactory, type FlatObject, type IOSchema, type InputSecurity, InputValidationError, Integration, type LoggerOverrides, type Metadata, type Method, type MiddlewareDefinition, MissingPeerError, type MockOverrides, type OAuth2Security, type OpenIdSecurity, OutputValidationError, type ResultHandlerDefinition, type Routing, RoutingError, ServeStatic, type ServerConfig, arrayEndpointsFactory, arrayResultHandler, attachRouting, createConfig, createLogger, createMiddleware, createResultHandler, createServer, defaultEndpointsFactory, defaultResultHandler, ez, getExamples, getMessageFromError, getStatusCodeFromError, testEndpoint, withMeta };
869
+ export { AbstractEndpoint, type ApiResponse, type AppConfig, type BasicSecurity, type BearerSecurity, type CommonConfig, type CookieSecurity, type CustomHeaderSecurity, DependsOnMethod, Documentation, DocumentationError, EndpointsFactory, type FlatObject, type IOSchema, type InputSecurity, InputValidationError, Integration, type LoggerOverrides, type Metadata, type Method, type MiddlewareDefinition, MissingPeerError, type MockOverrides, type OAuth2Security, type OpenIdSecurity, OutputValidationError, type ResultHandlerDefinition, type Routing, RoutingError, ServeStatic, type ServerConfig, arrayEndpointsFactory, arrayResultHandler, attachRouting, createConfig, createLogger, createMiddleware, createResultHandler, createServer, defaultEndpointsFactory, defaultResultHandler, ez, getExamples, getMessageFromError, getStatusCodeFromError, testEndpoint };