• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RobinTail / express-zod-api / 13444086320

20 Feb 2025 08:28PM UTC coverage: 100.0%. First build
13444086320

Pull #2425

github

web-flow
Merge 60a537c42 into 23732b2fe
Pull Request #2425: Fix: async `errorHandler` for not found case

1253 of 1279 branches covered (97.97%)

1 of 1 new or added line in 1 file covered. (100.0%)

4223 of 4223 relevant lines covered (100.0%)

210.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/express-zod-api/src/deep-checks.ts
1
import { fail } from "node:assert/strict"; // eslint-disable-line no-restricted-syntax -- acceptable
4✔
2
import { z } from "zod";
3
import { EmptyObject } from "./common-helpers";
4
import { ezDateInBrand } from "./date-in-schema";
4✔
5
import { ezDateOutBrand } from "./date-out-schema";
4✔
6
import { ezFileBrand } from "./file-schema";
4✔
7
import { IOSchema } from "./io-schema";
8
import { metaSymbol } from "./metadata";
4✔
9
import { ProprietaryBrand } from "./proprietary-schemas";
10
import { ezRawBrand } from "./raw-schema";
4✔
11
import { HandlingRules, NextHandlerInc, SchemaHandler } from "./schema-walker";
12
import { ezUploadBrand } from "./upload-schema";
4✔
13

14
/** @desc Check is a schema handling rule returning boolean */
15
type Check = SchemaHandler<boolean>;
16

17
const onSomeUnion: Check = (
4✔
18
  schema:
56✔
19
    | z.ZodUnion<z.ZodUnionOptions>
20
    | z.ZodDiscriminatedUnion<string, z.ZodDiscriminatedUnionOption<string>[]>,
21
  { next },
56✔
22
) => schema.options.some(next);
56✔
23

24
const onIntersection: Check = (
4✔
25
  { _def }: z.ZodIntersection<z.ZodTypeAny, z.ZodTypeAny>,
96✔
26
  { next },
96✔
27
) => [_def.left, _def.right].some(next);
96✔
28

29
const onWrapped: Check = (
4✔
30
  schema:
24✔
31
    | z.ZodOptional<z.ZodTypeAny>
32
    | z.ZodNullable<z.ZodTypeAny>
33
    | z.ZodReadonly<z.ZodTypeAny>
34
    | z.ZodBranded<z.ZodTypeAny, string | number | symbol>,
35
  { next },
24✔
36
) => next(schema.unwrap());
24✔
37

38
const ioChecks: HandlingRules<boolean, EmptyObject, z.ZodFirstPartyTypeKind> = {
4✔
39
  ZodObject: ({ shape }: z.ZodObject<z.ZodRawShape>, { next }) =>
4✔
40
    Object.values(shape).some(next),
1,408✔
41
  ZodUnion: onSomeUnion,
4✔
42
  ZodDiscriminatedUnion: onSomeUnion,
4✔
43
  ZodIntersection: onIntersection,
4✔
44
  ZodEffects: (schema: z.ZodEffects<z.ZodTypeAny>, { next }) =>
4✔
45
    next(schema.innerType()),
40✔
46
  ZodOptional: onWrapped,
4✔
47
  ZodNullable: onWrapped,
4✔
48
  ZodRecord: ({ valueSchema }: z.ZodRecord, { next }) => next(valueSchema),
4✔
49
  ZodArray: ({ element }: z.ZodArray<z.ZodTypeAny>, { next }) => next(element),
4✔
50
  ZodDefault: ({ _def }: z.ZodDefault<z.ZodTypeAny>, { next }) =>
4✔
51
    next(_def.innerType),
4✔
52
};
4✔
53

54
interface NestedSchemaLookupProps {
55
  condition?: (schema: z.ZodType) => boolean;
56
  rules?: HandlingRules<
57
    boolean,
58
    EmptyObject,
59
    z.ZodFirstPartyTypeKind | ProprietaryBrand
60
  >;
61
  maxDepth?: number;
62
  depth?: number;
63
}
64

65
/** @desc The optimized version of the schema walker for boolean checks */
66
export const hasNestedSchema = (
4✔
67
  subject: z.ZodType,
3,088✔
68
  {
3,088✔
69
    condition,
3,088✔
70
    rules = ioChecks,
3,088✔
71
    depth = 1,
3,088✔
72
    maxDepth = Number.POSITIVE_INFINITY,
3,088✔
73
  }: NestedSchemaLookupProps,
3,088✔
74
): boolean => {
3,088✔
75
  if (condition?.(subject)) return true;
3,088✔
76
  const handler =
3,012✔
77
    depth < maxDepth
3,012✔
78
      ? rules[subject._def[metaSymbol]?.brand as keyof typeof rules] ||
2,884✔
79
        ("typeName" in subject._def &&
2,864✔
80
          rules[subject._def.typeName as keyof typeof rules])
2,864✔
81
      : undefined;
128✔
82
  if (handler) {
3,088✔
83
    return handler(subject, {
1,732✔
84
      next: (schema) =>
1,732✔
85
        hasNestedSchema(schema, {
1,952✔
86
          condition,
1,952✔
87
          rules,
1,952✔
88
          maxDepth,
1,952✔
89
          depth: depth + 1,
1,952✔
90
        }),
1,952✔
91
    } as EmptyObject & NextHandlerInc<boolean>);
1,732✔
92
  }
1,732✔
93
  return false;
1,280✔
94
};
1,280✔
95

96
export const hasUpload = (subject: IOSchema) =>
4✔
97
  hasNestedSchema(subject, {
340✔
98
    condition: (schema) => schema._def[metaSymbol]?.brand === ezUploadBrand,
340✔
99
  });
340✔
100

101
export const hasRaw = (subject: IOSchema) =>
4✔
102
  hasNestedSchema(subject, {
348✔
103
    condition: (schema) => schema._def[metaSymbol]?.brand === ezRawBrand,
348✔
104
    maxDepth: 3,
348✔
105
  });
348✔
106

107
/** @throws AssertionError with incompatible schema constructor */
108
export const assertJsonCompatible = (subject: IOSchema, dir: "in" | "out") => {
4✔
109
  const lazies = new WeakSet<z.ZodLazy<z.ZodTypeAny>>();
384✔
110
  return hasNestedSchema(subject, {
384✔
111
    maxDepth: 300,
384✔
112
    rules: {
384✔
113
      ...ioChecks,
384✔
114
      ZodBranded: onWrapped,
384✔
115
      ZodReadonly: onWrapped,
384✔
116
      ZodCatch: ({ _def: { innerType } }: z.ZodCatch<z.ZodTypeAny>, { next }) =>
384✔
117
        next(innerType),
4✔
118
      ZodPipeline: (
384✔
119
        { _def }: z.ZodPipeline<z.ZodTypeAny, z.ZodTypeAny>,
4✔
120
        { next },
4✔
121
      ) => next(_def[dir]),
4✔
122
      ZodLazy: (lazy: z.ZodLazy<z.ZodTypeAny>, { next }) =>
384✔
123
        lazies.has(lazy) ? false : lazies.add(lazy) && next(lazy.schema),
4!
124
      ZodTuple: ({ items, _def: { rest } }: z.AnyZodTuple, { next }) =>
384✔
125
        [...items].concat(rest ?? []).some(next),
8✔
126
      ZodEffects: { out: undefined, in: ioChecks.ZodEffects }[dir],
384✔
127
      ZodNaN: () => fail("z.nan()"),
384✔
128
      ZodSymbol: () => fail("z.symbol()"),
384✔
129
      ZodFunction: () => fail("z.function()"),
384✔
130
      ZodMap: () => fail("z.map()"),
384✔
131
      ZodSet: () => fail("z.set()"),
384✔
132
      ZodBigInt: () => fail("z.bigint()"),
384✔
133
      ZodVoid: () => fail("z.void()"),
384✔
134
      ZodPromise: () => fail("z.promise()"),
384✔
135
      ZodNever: () => fail("z.never()"),
384✔
136
      ZodDate: () => dir === "in" && fail("z.date()"),
384✔
137
      [ezDateOutBrand]: () => dir === "in" && fail("ez.dateOut()"),
384✔
138
      [ezDateInBrand]: () => dir === "out" && fail("ez.dateIn()"),
384✔
139
      [ezRawBrand]: () => dir === "out" && fail("ez.raw()"),
384✔
140
      [ezUploadBrand]: () => dir === "out" && fail("ez.upload()"),
384✔
141
      [ezFileBrand]: () => false,
384✔
142
    },
384✔
143
  });
384✔
144
};
384✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc