Skip to content

Commit 224351e

Browse files
ochafikclaude
andcommitted
feat: add discriminatedUnion transform for tagged unions
Add DISCRIMINATED_UNIONS config to convert z.union() to z.discriminatedUnion() for better performance and error messages in tagged unions: - ContentBlockSchema -> discriminatedUnion('type') - SamplingMessageContentBlockSchema -> discriminatedUnion('type') Re-export SamplingMessageContentBlockSchema from generated (now compatible). Types.ts reduced from ~1275 to ~1267 lines. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent cc49983 commit 224351e

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

scripts/generate-schemas.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ const STRICT_SCHEMAS = [
100100
'EmptyResultSchema',
101101
];
102102

103+
/**
104+
* Schemas that should use z.discriminatedUnion instead of z.union for better performance.
105+
* Maps schema name to the discriminator field name.
106+
*/
107+
const DISCRIMINATED_UNIONS: Record<string, string> = {
108+
'SamplingContentSchema': 'type',
109+
'SamplingMessageContentBlockSchema': 'type',
110+
'ContentBlockSchema': 'type',
111+
};
112+
103113
// =============================================================================
104114
// Pre-processing: Transform spec types to SDK-compatible hierarchy
105115
// =============================================================================
@@ -273,6 +283,7 @@ const AST_TRANSFORMS: Transform[] = [
273283
transformUnionToEnum,
274284
applyFieldOverrides,
275285
addStrictToSchemas,
286+
convertToDiscriminatedUnion,
276287
];
277288

278289
/**
@@ -503,6 +514,30 @@ function addStrictToSchemas(sourceFile: SourceFile): void {
503514
}
504515
}
505516

517+
/**
518+
* Convert z.union() to z.discriminatedUnion() for specified schemas.
519+
* This provides better performance and error messages for tagged unions.
520+
*/
521+
function convertToDiscriminatedUnion(sourceFile: SourceFile): void {
522+
for (const [schemaName, discriminator] of Object.entries(DISCRIMINATED_UNIONS)) {
523+
const varDecl = sourceFile.getVariableDeclaration(schemaName);
524+
if (!varDecl) continue;
525+
526+
const initializer = varDecl.getInitializer();
527+
if (!initializer) continue;
528+
529+
const text = initializer.getText();
530+
531+
// Match z.union([...]) pattern and convert to z.discriminatedUnion('discriminator', [...])
532+
const unionMatch = text.match(/^z\.union\(\s*\[([\s\S]*)\]\s*\)$/);
533+
if (unionMatch) {
534+
const members = unionMatch[1];
535+
varDecl.setInitializer(`z.discriminatedUnion('${discriminator}', [${members}])`);
536+
console.log(` ✓ Converted ${schemaName} to discriminatedUnion('${discriminator}')`);
537+
}
538+
}
539+
}
540+
506541
// =============================================================================
507542
// Main
508543
// =============================================================================

src/generated/sdk.schemas.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,7 +2115,7 @@ export const ResourceLinkSchema = ResourceSchema.extend({
21152115
/**
21162116
* @category Content
21172117
*/
2118-
export const ContentBlockSchema = z.union([
2118+
export const ContentBlockSchema = z.discriminatedUnion('type', [
21192119
TextContentSchema,
21202120
ImageContentSchema,
21212121
AudioContentSchema,
@@ -2384,7 +2384,7 @@ export const PromptMessageSchema = z.object({
23842384
content: ContentBlockSchema
23852385
});
23862386

2387-
export const SamplingMessageContentBlockSchema = z.union([
2387+
export const SamplingMessageContentBlockSchema = z.discriminatedUnion('type', [
23882388
TextContentSchema,
23892389
ImageContentSchema,
23902390
AudioContentSchema,

src/types.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ import {
192192
UnsubscribeRequestParamsSchema,
193193
ResourceUpdatedNotificationParamsSchema,
194194
SetLevelRequestParamsSchema,
195+
// Sampling schemas (now using discriminatedUnion)
196+
SamplingMessageContentBlockSchema,
195197
} from './generated/sdk.schemas.js';
196198

197199
// Alias RequestParamsSchema to BaseRequestParamsSchema for internal use
@@ -336,6 +338,7 @@ export {
336338
UnsubscribeRequestParamsSchema,
337339
ResourceUpdatedNotificationParamsSchema,
338340
SetLevelRequestParamsSchema,
341+
SamplingMessageContentBlockSchema,
339342
};
340343

341344
export const LATEST_PROTOCOL_VERSION = '2025-11-25';
@@ -820,19 +823,8 @@ export type ListChangedHandlers = {
820823
*/
821824
export const SamplingContentSchema = z.discriminatedUnion('type', [TextContentSchema, ImageContentSchema, AudioContentSchema]);
822825

823-
/**
824-
* Content block types allowed in sampling messages.
825-
* This includes text, image, audio, tool use requests, and tool results.
826-
*/
827-
export const SamplingMessageContentBlockSchema = z.discriminatedUnion('type', [
828-
TextContentSchema,
829-
ImageContentSchema,
830-
AudioContentSchema,
831-
ToolUseContentSchema,
832-
ToolResultContentSchema
833-
]);
834-
835-
// Note: SamplingMessageSchema, CreateMessageRequestParamsSchema, CreateMessageRequestSchema,
826+
// Note: SamplingMessageContentBlockSchema (using discriminatedUnion),
827+
// SamplingMessageSchema, CreateMessageRequestParamsSchema, CreateMessageRequestSchema,
836828
// CreateMessageResultSchema are re-exported from generated.
837829

838830
/**

0 commit comments

Comments
 (0)