Skip to content

fix(zod): a couple of improvements#2391

Merged
ymc9 merged 2 commits intodevfrom
fix/zod-improvements
Feb 21, 2026
Merged

fix(zod): a couple of improvements#2391
ymc9 merged 2 commits intodevfrom
fix/zod-improvements

Conversation

@ymc9
Copy link
Copy Markdown
Member

@ymc9 ymc9 commented Feb 21, 2026

  • Add "create" and "update" variants to model schemas
  • Simplify dependencies
  • Translate ZModel meta description into zod meta

Summary by CodeRabbit

  • New Features

    • Support for description metadata (@meta, @@meta) on schemas, model fields, create/update shapes, typedefs and enums.
    • Added TypeScript types to improve strongly-typed Zod schema generation.
  • Refactor

    • Streamlined runtime dependencies and unified internal error handling.
  • Tests

    • Added tests verifying propagation of description metadata across schemas and fields.

- Add "create" and "update" variants to model schemas
- Simplify dependencies
- Translate ZModel meta description into zod meta
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Feb 21, 2026

Claude finished @ymc9's task —— View job


PR Review: Zod Schema Improvements

Todo List:

  • Analyze package.json dependency changes
  • Review new types.ts file structure and exports
  • Examine factory.ts refactoring and new functionality
  • Check utils.ts improvements and changes
  • Review test coverage additions
  • Evaluate schema changes and their impact
  • Assess security and performance implications
  • Provide comprehensive feedback

Code Review Feedback

🎯 Overall Assessment: Excellent refactoring with solid architecture improvements

This PR delivers exactly what it promises with clean, well-tested implementation of create/update schema variants, dependency simplification, and meta description support.

Strengths

Architecture & Design:

  • Excellent type safety in /packages/zod/src/types.ts:20-127 - Complex conditional types properly handle field optionality for different schema variants
  • Clean separation - New GetModelCreateFieldsShape and GetModelUpdateFieldsShape types clearly differentiate between full models, creation payloads, and update payloads
  • Smart field filtering - Create/update schemas correctly exclude relation fields (packages/zod/src/factory.ts:74-76, 99-101)

Implementation Quality:

  • Proper dependency injection - Schema factory cleanly uses SchemaAccessor instead of direct schema access (packages/zod/src/factory.ts:34-38)
  • Consistent patterns - All three schema methods (model/create/update) follow the same structure and apply validation consistently
  • Meta description integration - Clean implementation in packages/zod/src/factory.ts:224-247 that translates ZModel @meta attributes to Zod metadata

Testing:

  • Comprehensive coverage - New tests cover all meta description scenarios for models, fields, types, and enums (packages/zod/test/factory.test.ts:555-610)
  • Edge case handling - Tests verify descriptions are only set when present and work across all schema variants

🔧 Minor Improvements & Considerations

Type Safety:

  • The type casting in factory.ts:64,89,112 is necessary but could be documented with inline comments explaining why the cast is safe
  • Consider adding runtime assertions to validate that the type system guarantees hold at runtime

Performance:

  • Positive: Dependency simplification removes unused packages, reducing bundle size
  • Neutral: Lazy schema creation for relations is appropriate and prevents circular dependency issues
  • Consideration: Multiple schema variants may increase memory usage, but this is necessary for the intended functionality

Code Organization:

  • Good: Moving complex types to dedicated types.ts file improves maintainability
  • Good: Utils refactoring maintains clean separation of concerns

🔍 Security & Error Handling

Security:

  • ✅ No security concerns - all changes are type-level or validation-related
  • ✅ Input validation properly maintained across all schema variants
  • ✅ No exposure of sensitive operations or data

Error Handling:

  • ✅ Proper error handling with SchemaFactoryError for unsupported field types (packages/zod/src/factory.ts:168)
  • ✅ Graceful handling of missing attributes in meta description extraction (packages/zod/src/factory.ts:225)

📝 Specific Technical Notes

  1. Create Schema Logic (packages/zod/src/factory.ts:79-80): Correctly handles fields with defaults, optional fields, and updatedAt fields as optional in create operations
  2. Update Schema Logic (packages/zod/src/factory.ts:104): Appropriately makes all scalar fields optional for update operations
  3. Dependency Cleanup: Removal of 4 dependencies in package.json is clean and doesn't affect functionality

🎯 Verdict

This is a high-quality refactoring that:

  • ✅ Adds valuable functionality (create/update schema variants)
  • ✅ Improves code organization and maintainability
  • ✅ Maintains backward compatibility
  • ✅ Includes comprehensive test coverage
  • ✅ Follows established patterns and conventions

Recommendation: ✅ Approve - This PR is ready to merge. The implementation is solid, well-tested, and follows best practices.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 21, 2026

📝 Walkthrough

Walkthrough

Adds description metadata support to Zod schema generation, replaces ts-pattern dispatch with explicit switches, renames ZodSchemaError→SchemaFactoryError, introduces comprehensive TypeScript zod-typing utilities, and updates tests/schema fixtures to include @meta/@@meta annotations.

Changes

Cohort / File(s) Summary
Manifest
packages/zod/package.json
Removes three runtime dependencies (@zenstackhq/common-helpers, json-stable-stringify, ts-pattern) from the package manifest.
Error API
packages/zod/src/error.ts
Renames exported error class from ZodSchemaError to SchemaFactoryError (no behavior change).
Types
packages/zod/src/types.ts, packages/zod/src/index.ts
Adds a comprehensive TypeScript layer for mapping schema fields to Zod types (GetModel*/GetTypeDef* utilities, MapFieldTypeToZod, Json/Enum Zod types) and a type-only re-export in index.
Factory / Schema Generation
packages/zod/src/factory.ts
Implements description metadata extraction/apply logic (@meta/@@meta) across field, type-def, enum, model, create, and update schemas; restructures type handling with exhaustive switch and throws SchemaFactoryError for unsupported types.
Utilities / Expression Evaluation
packages/zod/src/utils.ts
Replaces ts-pattern matching with explicit switch-case dispatch for expression evaluation and validations, introduces local invariant helper, and switches error throws to SchemaFactoryError.
Tests & Fixtures
packages/zod/test/factory.test.ts, packages/zod/test/schema/schema.ts, packages/zod/test/schema/schema.zmodel
Adds/extends tests for meta description propagation; updates schema fixtures with @meta/@@meta annotations and expands User model fields used in tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I nibble on types and hop through the code,
I tuck little descriptions where schemas erode,
From patterns to switches I tidy each thread,
Now models tell stories where silence once fed.
Hop, hop—documentation blossoms ahead! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'a couple of improvements' is vague and generic, failing to convey the main changes such as adding create/update schema variants, simplifying dependencies, or translating meta descriptions. Consider revising the title to be more specific and descriptive, such as 'Add create/update schema variants and translate ZModel meta descriptions to Zod' or similar to better communicate the primary changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/zod-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/zod/test/factory.test.ts (1)

612-637: 🛠️ Refactor suggestion | 🟠 Major

Missing functional tests for makeModelCreateSchema and makeModelUpdateSchema.

The new makeModelCreateSchema and makeModelUpdateSchema methods are only tested for @meta description propagation. There are no tests verifying their core behavior:

  • Create schema: fields with @default should be optional; relation fields should be excluded; required fields without defaults should remain required.
  • Update schema: all non-relation fields should be optional; relation fields should be excluded.

These are critical behavioral contracts that should have test coverage.

Would you like me to generate functional test cases for makeModelCreateSchema and makeModelUpdateSchema, or open a tracking issue?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zod/test/factory.test.ts` around lines 612 - 637, Add functional
tests for factory.makeModelCreateSchema and factory.makeModelUpdateSchema that
verify core behavior: for makeModelCreateSchema assert that fields annotated
with `@default` become optional, relation fields are excluded from the schema, and
required scalar fields without defaults remain required; for
makeModelUpdateSchema assert that all non-relation scalar fields are optional
and relation fields are excluded. Use the existing factory instance (e.g.,
factory.makeModelCreateSchema('ModelName')) and zod safeParse/type inference
checks to assert presence/absence and optional vs required status for specific
fields (include at least one defaulted field, one required non-default field,
and one relation field per test). Ensure tests also assert z.infer types match
expected unions/optionality where appropriate.
🧹 Nitpick comments (3)
packages/zod/src/factory.ts (1)

67-90: makeModelCreateSchema re-applies .optional() on top of cardinality already applied by makeScalarFieldSchema.

For optional fields, makeScalarFieldSchemaapplyCardinality already wraps with .nullable().optional(). Then line 79-80 adds another .optional(), resulting in .nullable().optional().optional(). The behavior is correct at runtime (extra .optional() is a no-op), but it adds unnecessary nesting to the schema chain.

Consider extracting a makeBaseFieldSchema (without cardinality) to use in create/update methods, letting them apply cardinality themselves.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zod/src/factory.ts` around lines 67 - 90, makeModelCreateSchema
currently calls makeScalarFieldSchema (which already applies cardinality via
applyCardinality), then unconditionally calls .optional() again for
optional/default/updatedAt fields, producing redundant optional nesting;
refactor by introducing a new helper makeBaseFieldSchema (or similar) that
returns the scalar zod schema without applying cardinality, update
makeScalarFieldSchema to delegate to makeBaseFieldSchema + applyCardinality, and
change makeModelCreateSchema to use makeBaseFieldSchema so it can apply
.optional() only once for fields with fieldDef.optional || fieldDef.default !==
undefined || fieldDef.updatedAt; update any other callers accordingly to use
makeScalarFieldSchema when cardinality is desired.
packages/zod/src/types.ts (1)

51-58: Potential double ZodOptional wrapping for optional fields with defaults in GetModelCreateFieldsShape.

When a field is both optional (via ModelFieldIsOptional) and has a default (FieldHasDefault), this produces ZodOptional<ZodOptional<ZodNullable<T>>>. The inner ZodOptionalAndNullableIf adds ZodOptional<ZodNullable<T>>, then ZodOptionalIf wraps it in another ZodOptional. While functionally equivalent at runtime, this results in a less precise inferred type. The same pattern applies to GetModelUpdateFieldsShape (lines 60-66).

This is a type-level concern only and unlikely to cause real issues, but worth noting for type hygiene.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zod/src/types.ts` around lines 51 - 58, GetModelCreateFieldsShape
(and similarly GetModelUpdateFieldsShape) currently nests ZodOptionalIf around
ZodOptionalAndNullableIf, producing double ZodOptional wrappers for fields that
are both optional and have defaults; fix by ensuring optional wrapping happens
exactly once — either by changing the composition so ZodOptionalIf is applied to
the base MapModelFieldToZod<...> before ZodOptionalAndNullableIf runs, or by
updating the helpers (ZodOptionalIf / ZodOptionalAndNullableIf) so one
recognizes and no-ops when the inner type is already optional; locate
GetModelCreateFieldsShape / GetModelUpdateFieldsShape and adjust the ordering or
the helper logic to prevent emitting ZodOptional<ZodOptional<...>> for fields
where ModelFieldIsOptional and FieldHasDefault are both true.
packages/zod/src/utils.ts (1)

66-68: ReDoS risk: new RegExp(pattern) constructed from schema-defined input.

Both occurrences (line 68 in addStringValidation and line 448 in evalCall) construct a RegExp from a pattern string originating from ZModel schema attributes. While these patterns come from the schema author (not end-user input), a malicious or accidentally catastrophic regex could cause excessive backtracking.

If schema authors are trusted, this is low risk. Otherwise, consider wrapping in a try/catch with a timeout or validating the pattern complexity.

Also applies to: 441-448

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zod/src/utils.ts` around lines 66 - 68, The code constructs new
RegExp(pattern) from schema-provided strings in addStringValidation and
evalCall, which can cause ReDoS or throw on invalid patterns; wrap the RegExp
construction in a try/catch around both occurrences (the call sites in
addStringValidation and in evalCall) and validate the pattern before use (e.g.,
reject empty/obviously catastrophic patterns or use a safe regex
library/checker), logging or throwing a clear error that includes the
attribute/context when compilation fails, and ensure a safe fallback path (skip
applying the regex or fail schema validation) rather than allowing an unhandled
exception or expensive regex to be used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/zod/src/types.ts`:
- Line 42: The code uses internal Zod marker types z.core.$strict and
z.core.$loose (seen in types.ts around the occurrences at lines referenced and
in factory.ts usages); replace those internal markers with stable, public APIs:
use z.strictObject(...) or z.looseObject(...), or call .strict() / .loose() on
the relevant z.object(...) schemas, and update any type parameter references in
functions or type aliases (e.g., the symbols currently referencing
z.core.$strict / z.core.$loose in types.ts and factory.ts) to the corresponding
public schema types so the public API no longer depends on internal z.core
markers.

In `@packages/zod/src/utils.ts`:
- Around line 348-385: The switch in evalBinary currently lacks a default branch
so unknown operators silently return undefined; add a default: throw new
Error(...) to the switch inside evalBinary (the function handling expr.op) so it
throws a clear error for unsupported/unknown binary operators, include the
operator value in the thrown message to aid debugging.
- Around line 415-439: The code uses getArgValue<string>(expr.args?.[1])! to
obtain the search term which can be undefined for non-literal args; remove the
non-null assertion and after retrieving search (and caseInsensitive) add an
invariant that ensures search is a string (e.g., invariant(typeof search ===
'string', `"${f}" search argument must be a string literal`)) and similarly
validate the resolved caseInsensitive is boolean if present; update the block
around getArgValue, search, and applyStringOp (referencing getArgValue,
expr.args?.[1], search, caseInsensitive, and applyStringOp) to fail fast with a
clear error when non-literal or wrong-type args are passed.

---

Outside diff comments:
In `@packages/zod/test/factory.test.ts`:
- Around line 612-637: Add functional tests for factory.makeModelCreateSchema
and factory.makeModelUpdateSchema that verify core behavior: for
makeModelCreateSchema assert that fields annotated with `@default` become
optional, relation fields are excluded from the schema, and required scalar
fields without defaults remain required; for makeModelUpdateSchema assert that
all non-relation scalar fields are optional and relation fields are excluded.
Use the existing factory instance (e.g.,
factory.makeModelCreateSchema('ModelName')) and zod safeParse/type inference
checks to assert presence/absence and optional vs required status for specific
fields (include at least one defaulted field, one required non-default field,
and one relation field per test). Ensure tests also assert z.infer types match
expected unions/optionality where appropriate.

---

Nitpick comments:
In `@packages/zod/src/factory.ts`:
- Around line 67-90: makeModelCreateSchema currently calls makeScalarFieldSchema
(which already applies cardinality via applyCardinality), then unconditionally
calls .optional() again for optional/default/updatedAt fields, producing
redundant optional nesting; refactor by introducing a new helper
makeBaseFieldSchema (or similar) that returns the scalar zod schema without
applying cardinality, update makeScalarFieldSchema to delegate to
makeBaseFieldSchema + applyCardinality, and change makeModelCreateSchema to use
makeBaseFieldSchema so it can apply .optional() only once for fields with
fieldDef.optional || fieldDef.default !== undefined || fieldDef.updatedAt;
update any other callers accordingly to use makeScalarFieldSchema when
cardinality is desired.

In `@packages/zod/src/types.ts`:
- Around line 51-58: GetModelCreateFieldsShape (and similarly
GetModelUpdateFieldsShape) currently nests ZodOptionalIf around
ZodOptionalAndNullableIf, producing double ZodOptional wrappers for fields that
are both optional and have defaults; fix by ensuring optional wrapping happens
exactly once — either by changing the composition so ZodOptionalIf is applied to
the base MapModelFieldToZod<...> before ZodOptionalAndNullableIf runs, or by
updating the helpers (ZodOptionalIf / ZodOptionalAndNullableIf) so one
recognizes and no-ops when the inner type is already optional; locate
GetModelCreateFieldsShape / GetModelUpdateFieldsShape and adjust the ordering or
the helper logic to prevent emitting ZodOptional<ZodOptional<...>> for fields
where ModelFieldIsOptional and FieldHasDefault are both true.

In `@packages/zod/src/utils.ts`:
- Around line 66-68: The code constructs new RegExp(pattern) from
schema-provided strings in addStringValidation and evalCall, which can cause
ReDoS or throw on invalid patterns; wrap the RegExp construction in a try/catch
around both occurrences (the call sites in addStringValidation and in evalCall)
and validate the pattern before use (e.g., reject empty/obviously catastrophic
patterns or use a safe regex library/checker), logging or throwing a clear error
that includes the attribute/context when compilation fails, and ensure a safe
fallback path (skip applying the regex or fail schema validation) rather than
allowing an unhandled exception or expensive regex to be used.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Feb 21, 2026

Claude finished @ymc9's task —— View job


PR Review: Zod Schema Improvements

Todo List:

  • Analyze core changes and architecture
  • Review code quality and implementation patterns
  • Identify potential bugs and issues
  • Assess performance implications
  • Check security considerations
  • Evaluate test coverage
  • Provide actionable feedback

Summary

This PR delivers a solid implementation of create/update schema variants with meta description support. The architecture is well-designed, but there are several technical issues that should be addressed before merge.

Code Quality & Architecture ✅

Strengths:

  • Excellent type safety - Complex conditional types in packages/zod/src/types.ts:51-66 properly handle field optionality across different schema variants
  • Clean separation - New schema variants correctly exclude relation fields and apply appropriate field transformations
  • Consistent patterns - All three schema methods follow the same structure and validation approach
  • Good dependency management - Removal of 4 unused dependencies reduces bundle size

Bugs & Issues ⚠️

1. Critical: Missing default case in evalBinary (packages/zod/src/utils.ts:385)

Unlike other eval functions, evalBinary lacks a default case, causing unknown operators to silently return undefined.

// Add to switch statement:
default:
    throw new SchemaFactoryError(`Unsupported binary operator: ${expr.op}`);

2. High: Unsafe non-null assertion (packages/zod/src/utils.ts:426)

getArgValue<string>(expr.args?.[1])! can be undefined for non-literal expressions, causing runtime errors.

const search = getArgValue<string>(expr.args?.[1]);
invariant(search !== undefined, `"${f}" search argument must be a string literal`);

3. Medium: RegEx injection risk (packages/zod/src/utils.ts:68,450)

new RegExp(pattern) from schema input could cause ReDoS or throw on invalid patterns. Add try-catch protection.

4. Low: Redundant optional wrapping

In makeModelCreateSchema, fields get double .optional() calls - once from makeScalarFieldSchema cardinality and again explicitly.

Performance Considerations ✅

  • Positive: Dependency reduction (4 fewer packages) improves bundle size
  • Neutral: Schema variants increase memory footprint but provide necessary functionality
  • Good: Lazy relation schema creation prevents circular dependency issues

Security Assessment ✅

  • No security vulnerabilities - All changes are type-level or validation-related
  • Input validation maintained - All schema variants properly apply field validation
  • ReDoS risk noted above - Should be addressed but low impact since patterns come from trusted schema authors

Test Coverage ⚠️

Current coverage is good but incomplete:

  • ✅ Meta description propagation thoroughly tested (packages/zod/test/factory.test.ts:555-610)
  • ✅ Existing model schema functionality fully covered
  • Missing functional tests for makeModelCreateSchema and makeModelUpdateSchema core behavior

Needed test coverage:

  • Create schema: Verify fields with @default become optional, relation fields excluded, required fields remain required
  • Update schema: Verify all scalar fields become optional, relation fields excluded

Actionable Recommendations

Must Fix Before Merge:

  1. Add default case to evalBinary switch statement (packages/zod/src/utils.ts:385)
  2. Remove non-null assertion and add proper validation in string function handling (packages/zod/src/utils.ts:426)

Should Fix Before Merge:

  1. Add functional tests for create/update schema variants to verify core field transformation behavior
  2. Add try-catch protection around new RegExp() calls to prevent ReDoS/invalid pattern issues

Consider for Follow-up:

  1. Eliminate redundant optional wrapping in create schema method
  2. Replace internal Zod types (z.core.$strict) with public API alternatives for better API stability

Overall Assessment

This is a well-architected feature addition that successfully implements the stated goals. The type system is sophisticated and the meta description integration is clean. The identified issues are straightforward to fix and don't undermine the core design.

Recommendation: Approve after addressing critical bugs


Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/zod/src/utils.ts (2)

353-356: Consider strict equality to avoid implicit type coercion.

==/!= allows null == undefined → true, 0 == false → true, and "" == false → true. Unless ZModel semantics explicitly require coercive equality (e.g., null to match undefined field values), prefer ===/!== to prevent unexpected comparison results.

♻️ Proposed fix
         case '==':
-            return left == right;
+            return left === right;
         case '!=':
-            return left != right;
+            return left !== right;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zod/src/utils.ts` around lines 353 - 356, Replace the coercive
equality operators in the equality handling branch so the '==' case uses strict
equality between left and right and the '!=' case uses strict inequality; locate
the switch/case handling the '==' and '!=' operators (the branches returning
left == right and left != right) and change them to use === and !==
respectively, and run/update any tests that assume coercive comparisons if
necessary.

429-441: applyStringOp is missing a default branch, inferring boolean | undefined.

TypeScript may not narrow the closure-captured f from the outer switch, so applyStringOp can infer a return type of boolean | undefined. While Boolean(undefined) in applyValidation makes this safe at runtime, an explicit default guard makes the intent clear and avoids any silent false for an unsupported value.

♻️ Proposed fix
             const applyStringOp = (x: string, y: string) => {
                 switch (f) {
                     case 'startsWith':
                         return x.startsWith(y);
                     case 'endsWith':
                         return x.endsWith(y);
                     case 'contains':
                         return x.includes(y);
+                    default:
+                        throw new SchemaFactoryError(`Unsupported string op: ${f}`);
                 }
             };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zod/src/utils.ts` around lines 429 - 441, The switch inside
applyStringOp can return undefined when f isn't
'startsWith'|'endsWith'|'contains', causing applyStringOp to infer
boolean|undefined; add an explicit default branch in the switch (inside the
applyStringOp closure) that returns a safe boolean (e.g., false) or throws a
clear error so the function's return type is always boolean and intent is
explicit—update the switch handling of f to include this default branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/zod/src/utils.ts`:
- Around line 65-70: The `@regex` branch in utils.ts (case '@regex' where result =
result.regex(new RegExp(pattern))) and the similar use in evalCall must not
construct arbitrary RegExp instances directly because catastrophic backtracking
can cause ReDoS; validate or sanitize the pattern at schema-build time (e.g.,
using a safe-regex check or a safer engine like re2) and reject or throw a
descriptive error for unsafe/complex patterns, or document and enforce a
whitelist/limit on pattern features before calling new RegExp; apply the same
validation/guard to the pattern handling in evalCall so both call sites refuse
or transform unsafe patterns instead of directly instantiating RegExp.

---

Duplicate comments:
In `@packages/zod/src/utils.ts`:
- Around line 424-427: The non-null assertion on the getArgValue call hides
runtime undefineds; replace the `const search =
getArgValue<string>(expr.args?.[1])!` with a safe check: assign the result to a
variable (e.g., `const search = getArgValue<string>(expr.args?.[1])`) and then
assert/throw if it's undefined (use `invariant(search, \`"... ${f}" requires a
literal search string\`)` or similar) before any use (such as
`search.toLowerCase()`); also ensure the boolean caseInsensitive uses its parsed
value only after validating `search`. This change touches the getArgValue usage
and the surrounding invariant logic so the code never calls string methods on
undefined.
- Around line 448-449: Remove the non-null assertion after the getArgValue call
so that pattern is typed as possibly undefined (i.e., change const pattern =
getArgValue<string>(expr.args?.[1])! to without the '!'), and keep the runtime
guard invariant(pattern !== undefined, `"${f}" requires a pattern argument`);
this ensures the compile-time type matches the actual runtime check for
getArgValue, consistent with the same fix applied earlier around line 426.

---

Nitpick comments:
In `@packages/zod/src/utils.ts`:
- Around line 353-356: Replace the coercive equality operators in the equality
handling branch so the '==' case uses strict equality between left and right and
the '!=' case uses strict inequality; locate the switch/case handling the '=='
and '!=' operators (the branches returning left == right and left != right) and
change them to use === and !== respectively, and run/update any tests that
assume coercive comparisons if necessary.
- Around line 429-441: The switch inside applyStringOp can return undefined when
f isn't 'startsWith'|'endsWith'|'contains', causing applyStringOp to infer
boolean|undefined; add an explicit default branch in the switch (inside the
applyStringOp closure) that returns a safe boolean (e.g., false) or throws a
clear error so the function's return type is always boolean and intent is
explicit—update the switch handling of f to include this default branch.

@ymc9 ymc9 merged commit 7e838a6 into dev Feb 21, 2026
11 of 16 checks passed
@ymc9 ymc9 deleted the fix/zod-improvements branch February 21, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant