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

RobinTail / express-zod-api / 14709382549

28 Apr 2025 01:46PM UTC coverage: 99.829% (-0.2%) from 100.0%
14709382549

Pull #2589

github

web-flow
Merge de53d5783 into daa87c2ff
Pull Request #2589: Exp, Ref: deep checks using `toJSONSchema`

1206 of 1248 branches covered (96.63%)

51 of 57 new or added lines in 4 files covered. (89.47%)

7 existing lines in 1 file now uncovered.

4087 of 4094 relevant lines covered (99.83%)

257.66 hits per line

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

92.78
/express-zod-api/src/deep-checks.ts
1
import type {
2
  $ZodArray,
3
  $ZodCatch,
4
  $ZodDefault,
5
  $ZodDiscriminatedUnion,
6
  $ZodInterface,
7
  $ZodIntersection,
8
  $ZodLazy,
9
  $ZodNullable,
10
  $ZodObject,
11
  $ZodOptional,
12
  $ZodPipe,
13
  $ZodReadonly,
14
  $ZodRecord,
15
  $ZodTuple,
16
  $ZodType,
17
  $ZodUnion,
18
} from "@zod/core";
19
import { fail } from "node:assert/strict"; // eslint-disable-line no-restricted-syntax -- acceptable
4✔
20
import { globalRegistry } from "zod";
4✔
21
import { ezDateInBrand } from "./date-in-schema";
4✔
22
import { ezDateOutBrand } from "./date-out-schema";
4✔
23
import { ezFileBrand } from "./file-schema";
4✔
24
import { metaSymbol } from "./metadata";
4✔
25
import { ProprietaryBrand } from "./proprietary-schemas";
26
import { ezRawBrand } from "./raw-schema";
4✔
27
import {
28
  FirstPartyKind,
29
  HandlingRules,
30
  NextHandlerInc,
31
  SchemaHandler,
32
} from "./schema-walker";
33
import { ezUploadBrand } from "./upload-schema";
4✔
34

35
type CheckContext = { visited: WeakSet<object> };
36
/** @desc Check is a schema handling rule returning boolean */
37
type Check = SchemaHandler<boolean, CheckContext>;
38

39
const onSomeUnion: Check = (
4✔
NEW
UNCOV
40
  { _zod }: $ZodUnion | $ZodDiscriminatedUnion,
×
NEW
UNCOV
41
  { next },
×
NEW
UNCOV
42
) => _zod.def.options.some(next);
×
43

44
const onIntersection: Check = ({ _zod }: $ZodIntersection, { next }) =>
4✔
NEW
UNCOV
45
  [_zod.def.left, _zod.def.right].some(next);
×
46

47
const onWrapped: Check = (
4✔
48
  {
4✔
49
    _zod: { def },
4✔
50
  }: $ZodOptional | $ZodNullable | $ZodReadonly | $ZodDefault | $ZodCatch,
4✔
51
  { next },
4✔
52
) => next(def.innerType);
4✔
53

54
const ioChecks: HandlingRules<boolean, CheckContext, FirstPartyKind> = {
4✔
55
  object: ({ _zod }: $ZodObject, { next }) =>
4✔
56
    Object.values(_zod.def.shape).some(next),
704✔
57
  interface: (int: $ZodInterface, { next, visited }) =>
4✔
NEW
UNCOV
58
    visited.has(int)
×
UNCOV
59
      ? false
×
NEW
UNCOV
60
      : visited.add(int) && Object.values(int._zod.def.shape).some(next),
×
61
  union: onSomeUnion,
4✔
62
  intersection: onIntersection,
4✔
63
  optional: onWrapped,
4✔
64
  nullable: onWrapped,
4✔
65
  default: onWrapped,
4✔
66
  record: ({ _zod }: $ZodRecord, { next }) => next(_zod.def.valueType),
4✔
67
  array: ({ _zod }: $ZodArray, { next }) => next(_zod.def.element),
4✔
68
};
4✔
69

70
interface NestedSchemaLookupProps extends Partial<CheckContext> {
71
  condition?: (schema: $ZodType) => boolean;
72
  rules?: HandlingRules<
73
    boolean,
74
    CheckContext,
75
    FirstPartyKind | ProprietaryBrand
76
  >;
77
  maxDepth?: number;
78
  depth?: number;
79
}
80

81
/** @desc The optimized version of the schema walker for boolean checks */
82
export const hasNestedSchema = (
4✔
83
  subject: $ZodType,
1,320✔
84
  {
1,320✔
85
    condition,
1,320✔
86
    rules = ioChecks,
1,320✔
87
    depth = 1,
1,320✔
88
    maxDepth = Number.POSITIVE_INFINITY,
1,320✔
89
    visited = new WeakSet(),
1,320✔
90
  }: NestedSchemaLookupProps,
1,320✔
91
): boolean => {
1,320✔
92
  if (condition?.(subject)) return true;
1,320!
93
  if (depth >= maxDepth) return false;
1,320!
94
  const { brand } = globalRegistry.get(subject)?.[metaSymbol] ?? {};
1,320✔
95
  const handler =
1,320✔
96
    brand && brand in rules
1,320✔
97
      ? rules[brand as keyof typeof rules]
20✔
98
      : rules[subject._zod.def.type];
1,300✔
99
  if (handler) {
1,320✔
100
    return handler(subject, {
824✔
101
      visited,
824✔
102
      next: (schema) =>
824✔
103
        hasNestedSchema(schema, {
896✔
104
          condition,
896✔
105
          rules,
896✔
106
          maxDepth,
896✔
107
          visited,
896✔
108
          depth: depth + 1,
896✔
109
        }),
896✔
110
    } as CheckContext & NextHandlerInc<boolean>);
824✔
111
  }
824✔
112
  return false;
496✔
113
};
496✔
114

115
/** @throws AssertionError with incompatible schema constructor */
116
export const assertJsonCompatible = (subject: $ZodType, dir: "in" | "out") =>
4✔
117
  hasNestedSchema(subject, {
424✔
118
    maxDepth: 300,
424✔
119
    rules: {
424✔
120
      ...ioChecks,
424✔
121
      readonly: onWrapped,
424✔
122
      catch: onWrapped,
424✔
123
      pipe: ({ _zod }: $ZodPipe, { next }) => next(_zod.def[dir]),
424✔
124
      lazy: ({ _zod: { def } }: $ZodLazy, { next, visited }) =>
424✔
125
        visited.has(def.getter)
16✔
126
          ? false
4✔
127
          : visited.add(def.getter) && next(def.getter()),
12✔
128
      tuple: ({ _zod: { def } }: $ZodTuple, { next }) =>
424✔
129
        [...def.items].concat(def.rest ?? []).some(next),
8✔
130
      nan: () => fail("z.nan()"),
424✔
131
      symbol: () => fail("z.symbol()"),
424✔
132
      map: () => fail("z.map()"),
424✔
133
      set: () => fail("z.set()"),
424✔
134
      bigint: () => fail("z.bigint()"),
424✔
135
      void: () => fail("z.void()"),
424✔
136
      promise: () => fail("z.promise()"),
424✔
137
      never: () => fail("z.never()"),
424✔
138
      date: () => dir === "in" && fail("z.date()"),
424✔
139
      [ezDateOutBrand]: () => dir === "in" && fail("ez.dateOut()"),
424✔
140
      [ezDateInBrand]: () => dir === "out" && fail("ez.dateIn()"),
424✔
141
      [ezRawBrand]: () => dir === "out" && fail("ez.raw()"),
424✔
142
      [ezUploadBrand]: () => dir === "out" && fail("ez.upload()"),
424✔
143
      [ezFileBrand]: () => false,
424✔
144
    },
424✔
145
  });
424✔
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