Skip to content

fix: resolve consumer message schemas to the schema output type#472

Merged
CarlosGamero merged 4 commits into
mainfrom
fix/consumer-message-schema-output-type
Jun 12, 2026
Merged

fix: resolve consumer message schemas to the schema output type#472
CarlosGamero merged 4 commits into
mainfrom
fix/consumer-message-schema-output-type

Conversation

@CarlosGamero

@CarlosGamero CarlosGamero commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

ConsumerMessageSchema and AllConsumerMessageSchemas currently resolve to z.input of the consumer schema. However, consumers receive messages that have already been parsed by that schema (parseMessage calls schema.parse(...) before the handler is invoked), so the correct type is z.output.

For schemas without transforms z.input and z.output are identical, so this change is invisible to most consumers. They diverge once a schema uses transform/preprocess — for example a forward-compatible field that drops unknown enum variants instead of failing the whole event:

const PAYLOAD_SCHEMA = z.object({
  // ...
  context: z.preprocess(
    (v) => (isKnownContextType(v) ? v : undefined),
    CONTEXT_SCHEMA.optional(),
  ),
})

With z.input, the handler sees context: unknown (the input of a preprocess is always unknown), even though at runtime it receives the parsed z.output value. With this change the handler is typed with what it actually gets.

The same fix is applied to CommonEventDefinitionConsumerSchemaType, which had the same issue and types the handlers DomainEventEmitter invokes (EventHandler, SingleEventHandler, AnyEventHandler).

Publisher-side types (PublisherMessageSchema, AllPublisherMessageSchemas, CommonEventDefinitionPublisherSchemaType) intentionally stay z.input, since publishers pass the raw payload that DomainEventEmitter.emit parses.

Note on impact

This is a type-level-only change (no runtime behavior). It may surface new type errors for downstream code that relied on the input type of transforming consumer schemas — those were previously typed incorrectly. A minor version bump is probably appropriate.

Testing

  • Type tests added (vitest typecheck enabled in packages/schemas, following the existing core/sns setup):
    • lib/utils/messageTypeUtils.types.spec.tsConsumerMessageSchema resolves transformed fields to their output type, PublisherMessageSchema keeps the raw input, AllConsumerMessageSchemas resolves the parsed union.
    • lib/events/eventTypes.types.spec.ts — same coverage for CommonEventDefinitionConsumerSchemaType / CommonEventDefinitionPublisherSchemaType, plus SingleEventHandler / AnyEventHandler receiving the parsed event.
    • packages/core test/queues/HandlerContainer.types.spec.tsMessageHandlerConfigBuilder.addConfig infers the schema output as the handler message type (the path SNS/SQS consumer handlers go through).
  • packages/schemas: biome check + tsc + vitest all pass
  • packages/core, packages/sns, packages/sqs (including test consumers using ConsumerMessageSchema): tsc --noEmit passes

Follow-up

An integration-level type test on DomainEventEmitter itself (asserting that on/onAny handlers and the emit return type see the parsed event) cannot be added in this PR: packages/core depends on the published @message-queue-toolkit/schemas (^7.0.0), so such a test only compiles once a version containing this fix is released and the dependency is bumped. The equivalent semantics are pinned in eventTypes.types.spec.ts via SingleEventHandler/AnyEventHandler (the exact types on/onAny use). Happy to add the DomainEventEmitter spec in a follow-up PR after the release.

AI Assistance Tracking

We're running a metric to understand where AI assists our engineering work. Please select exactly one of the options below:

Mark "Yes" if AI helped in any part of this work, for example: generating code, refactoring, debugging support, explaining something, reviewing an idea, or suggesting an approach.

  • Yes, AI assisted with this PR
  • No, AI did not assist with this PR

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Corrected consumer message schema type validation to accurately reflect message parsing behavior.

Consumers receive messages that have already been parsed by the consumer
schema, so ConsumerMessageSchema and AllConsumerMessageSchemas should
resolve to z.output rather than z.input.

For schemas without transforms both types are identical, so this changes
nothing. They diverge once a schema uses transforms or preprocess (e.g. a
field that tolerantly drops unknown enum values): there z.input degrades
to unknown and breaks typing in message handlers, while the handler
actually receives the parsed output.

Publisher-side types intentionally stay z.input, since publishers pass
the raw payload that the schema parses on emit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@CarlosGamero, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 10 minutes and 46 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d06b0bb-4326-41af-b4f0-ca9c0fb9439a

📥 Commits

Reviewing files that changed from the base of the PR and between ab7d301 and d097e5e.

📒 Files selected for processing (4)
  • packages/schemas/lib/events/eventTypes.ts
  • packages/schemas/lib/events/eventTypes.types.spec.ts
  • packages/schemas/lib/utils/messageTypeUtils.types.spec.ts
  • packages/schemas/vitest.config.ts
📝 Walkthrough

Walkthrough

Updated consumer message schema utility types to derive message types from schema output rather than input. ConsumerMessageSchema and AllConsumerMessageSchemas generic types now use z.output<...> instead of z.input<...> to reflect that consumer messages are parsed by the consumer schema.

Changes

Consumer Message Type Output Extraction

Layer / File(s) Summary
Consumer message schema output types
packages/schemas/lib/utils/messageTypeUtils.ts
ConsumerMessageSchema and AllConsumerMessageSchemas generic types updated to derive message types from z.output<...> of the consumer schema instead of z.input<...>, reflecting that parsed consumer messages conform to the schema's output type.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰 Two types evolve from input's maze,
Now output shines in brighter ways,
Consumer schemas parse just right—
From input chains to output's light! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change—updating consumer message schemas from input to output types—using clear, specific terminology that reflects the primary objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/consumer-message-schema-output-type

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.

* Consumers receive messages already parsed by the consumer schema, hence the output type.
*/
export type ConsumerMessageSchema<MessageDefinitionType extends CommonEventDefinition> = z.input<
export type ConsumerMessageSchema<MessageDefinitionType extends CommonEventDefinition> = z.output<

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

can we add type test for this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yep, on it right now :D

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Tests added, and also added a few more on #473

@CarlosGamero CarlosGamero self-assigned this Jun 11, 2026
@CarlosGamero CarlosGamero merged commit 547ea16 into main Jun 12, 2026
8 checks passed
@CarlosGamero CarlosGamero deleted the fix/consumer-message-schema-output-type branch June 12, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants