Skip to content

Support x-pattern-message extension for regular expressions #4116

Closed

Description

@michaelboyles

Description

Other generators, such as the very popular openapi-generator have support for the x-pattern-message extension. This is a string which provides a human-readable description for a regex pattern. So rather than just have /[A-Za-z0-9]+/, you can describe that with "Must be alphanumeric".

This can be passed along to Zod

z.string().regex(/[A-Za-z0-9]+/, "Must be alphanumeric")

Valibot

v.regex(/[A-Za-z0-9]+/, "Must be alphanumeric")

and Arktype (a bit more involved)

type("string").narrow((value, ctx) => /[a-zA-Z0-9]/.test(value) && ctx.reject("Must be alphanumeric"))

Activity

  1. pullfrog commented on Jun 21, 2026

    @pullfrog
    Contributor

    Plan: Support x-pattern-message extension for regular expressions

    Background

    The x-pattern-message extension (popularized by openapi-generator) provides a human-readable description alongside a regex pattern constraint on string schemas. It passes a custom error message to validator .regex() calls.

    Current state: The OpenAPI parsers already copy all x-* extensions to the IR schema objects via parseExtensions(), so x-pattern-message is already accessible at codegen time — but no plugin reads it. The Zod and Valibot patternNode functions only emit the regex literal without a message argument.

    Changes by File

    1. Zod v3 — packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts

    patternNode (line 67): Read ctx.schema['x-pattern-message'] and pass as second arg to .regex():

    function patternNode(ctx: StringResolverContext): ChainResult {
      const { chain, schema } = ctx;
      if (!schema.pattern) return;
      const flags = /\\[pP]\{/.test(schema.pattern) ? 'u' : undefined;
      const message = schema['x-pattern-message'];
      return chain.current.attr(identifiers.regex).call(
        $.regexp(schema.pattern, flags),
        ...(message ? [$.literal(message as string)] : [])
      );
    }

    2. Zod v4 — packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts

    Same change as v3 in patternNode (line 74).

    3. Zod mini — packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts

    patternNode (line 77): Same approach. Note: mini wraps pattern in .check(), but the .regex() call itself still supports a message second arg.

    4. Valibot — packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts

    patternNode (line 67): Valibot's regex() action accepts (input, error) where error is a string message:

    function patternNode(ctx: StringResolverContext): PipeResult {
      const { schema } = ctx;
      const { v } = ctx.plugin.imports;
      if (!schema.pattern) return;
      const flags = /\\[pP]\{/.test(schema.pattern) ? 'u' : undefined;
      const message = schema['x-pattern-message'];
      return $(v).attr(identifiers.actions.regex).call(
        $.regexp(schema.pattern, flags),
        ...(message ? [$.literal(message as string)] : [])
      );
    }

    5. Test fixture update

    Add x-pattern-message to an existing pattern-bearing schema in specs/3.0.x/validators.json (e.g., the ^\d{3}-\d{2}-\d{4}$ pattern at line 14) and regenerate snapshots via pnpm tu.

    6. Schemas plugin — packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts

    Optionally add x-pattern-message to the stripSchema delete list for form mode — though x-pattern-message is a valid custom extension in JSON Schema output, so this may not be needed.

    What does NOT need changes

    • Parsers: parseExtensions() already copies all x-* keys to IR. No parser changes needed.
    • IR types: IRSchemaObject already extends SpecExtensions. No type changes needed.
    • TypeScript plugin: Correctly ignores pattern (TypeScript has no regex types).
    • Arktype: No arktype plugin exists in this repo — out of scope.

    Validation

    After changes: run pnpm tu -- @hey-api/openapi-ts-tests to update snapshots, then pnpm typecheck and pnpm test.

    Task list (4/4 completed)
    • Analyze the issue and gather context (read comments, events, learnings file)
    • Explore codebase: understand how validator plugins handle pattern/regex
    • Explore codebase: check for arktype plugin and schemas plugin extension stripping
    • Produce structured plan and report progress

    Pullfrog  | Implement plan ➔ | View workflow run | via Pullfrog | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

  2. mrlubos commented on Jun 21, 2026

    @mrlubos
    Member

    Should be easy enough, let's get it in

  3. pullfrog commented on Jun 21, 2026

    @pullfrog
    Contributor

    Implemented x-pattern-message support across all 4 validator plugins (Zod v3, Zod v4, Zod mini, Valibot). When a string schema has x-pattern-message, it's passed as the second arg to .regex() / v.regex(). Added test fixture coverage and updated 7 snapshots. Typecheck passes clean.

    PR: #4117

    Task list (10/10 completed)
    • Read relevant source files (Zod v3, Zod v4, Zod mini, Valibot patternNode functions)
    • Create branch for changes
    • Update Zod v3 patternNode to support x-pattern-message
    • Update Zod v4 patternNode to support x-pattern-message
    • Update Zod mini patternNode to support x-pattern-message
    • Update Valibot patternNode to support x-pattern-message
    • Add x-pattern-message to test fixture in specs/3.0.x/validators.json
    • Run snapshot update (pnpm tu -- @hey-api/openapi-ts-tests)
    • Run typecheck and tests
    • Commit, push, create PR

    Pullfrog  | View workflow run | via Pullfrog | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

  4. mrlubos commented on Jun 21, 2026

    @mrlubos
    Member

    This will be available in the next release or you can get it now with npm i @hey-api/openapi-ts@next

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions