@boostecom/provider 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +90 -0
  2. package/dist/index.cjs +2522 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +848 -0
  5. package/dist/index.d.ts +848 -0
  6. package/dist/index.js +2484 -0
  7. package/dist/index.js.map +1 -0
  8. package/docs/content/README.md +337 -0
  9. package/docs/content/agent-teams.mdx +324 -0
  10. package/docs/content/api.mdx +757 -0
  11. package/docs/content/best-practices.mdx +624 -0
  12. package/docs/content/examples.mdx +675 -0
  13. package/docs/content/guide.mdx +516 -0
  14. package/docs/content/index.mdx +99 -0
  15. package/docs/content/installation.mdx +246 -0
  16. package/docs/content/skills.mdx +548 -0
  17. package/docs/content/troubleshooting.mdx +588 -0
  18. package/docs/examples/README.md +499 -0
  19. package/docs/examples/abort-signal.ts +125 -0
  20. package/docs/examples/agent-teams.ts +122 -0
  21. package/docs/examples/basic-usage.ts +73 -0
  22. package/docs/examples/check-cli.ts +51 -0
  23. package/docs/examples/conversation-history.ts +69 -0
  24. package/docs/examples/custom-config.ts +90 -0
  25. package/docs/examples/generate-object-constraints.ts +209 -0
  26. package/docs/examples/generate-object.ts +211 -0
  27. package/docs/examples/hooks-callbacks.ts +63 -0
  28. package/docs/examples/images.ts +76 -0
  29. package/docs/examples/integration-test.ts +241 -0
  30. package/docs/examples/limitations.ts +150 -0
  31. package/docs/examples/logging-custom-logger.ts +99 -0
  32. package/docs/examples/logging-default.ts +55 -0
  33. package/docs/examples/logging-disabled.ts +74 -0
  34. package/docs/examples/logging-verbose.ts +64 -0
  35. package/docs/examples/long-running-tasks.ts +179 -0
  36. package/docs/examples/message-injection.ts +210 -0
  37. package/docs/examples/mid-stream-injection.ts +126 -0
  38. package/docs/examples/run-all-examples.sh +48 -0
  39. package/docs/examples/sdk-tools-callbacks.ts +49 -0
  40. package/docs/examples/skills-discovery.ts +144 -0
  41. package/docs/examples/skills-management.ts +140 -0
  42. package/docs/examples/stream-object.ts +80 -0
  43. package/docs/examples/streaming.ts +52 -0
  44. package/docs/examples/structured-output-repro.ts +227 -0
  45. package/docs/examples/tool-management.ts +215 -0
  46. package/docs/examples/tool-streaming.ts +132 -0
  47. package/docs/examples/zod4-compatibility-test.ts +290 -0
  48. package/docs/src/claude-code-language-model.test.ts +3883 -0
  49. package/docs/src/claude-code-language-model.ts +2586 -0
  50. package/docs/src/claude-code-provider.test.ts +97 -0
  51. package/docs/src/claude-code-provider.ts +179 -0
  52. package/docs/src/convert-to-claude-code-messages.images.test.ts +104 -0
  53. package/docs/src/convert-to-claude-code-messages.test.ts +193 -0
  54. package/docs/src/convert-to-claude-code-messages.ts +419 -0
  55. package/docs/src/errors.test.ts +213 -0
  56. package/docs/src/errors.ts +216 -0
  57. package/docs/src/index.test.ts +49 -0
  58. package/docs/src/index.ts +98 -0
  59. package/docs/src/logger.integration.test.ts +164 -0
  60. package/docs/src/logger.test.ts +184 -0
  61. package/docs/src/logger.ts +65 -0
  62. package/docs/src/map-claude-code-finish-reason.test.ts +120 -0
  63. package/docs/src/map-claude-code-finish-reason.ts +60 -0
  64. package/docs/src/mcp-helpers.test.ts +71 -0
  65. package/docs/src/mcp-helpers.ts +123 -0
  66. package/docs/src/message-injection.test.ts +460 -0
  67. package/docs/src/types.ts +447 -0
  68. package/docs/src/validation.test.ts +558 -0
  69. package/docs/src/validation.ts +360 -0
  70. package/package.json +124 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Example: Tool Management
3
+ *
4
+ * This example demonstrates how to manage tool permissions for Claude Code.
5
+ * The allowedTools and disallowedTools flags work for BOTH:
6
+ * - Built-in Claude tools (Bash, Edit, Read, Write, etc.)
7
+ * - MCP tools (mcp__serverName__toolName format)
8
+ *
9
+ * These are session-only permission overrides that use the same rule syntax
10
+ * as settings.json permissions.
11
+ */
12
+
13
+ import { streamText } from 'ai';
14
+ import { createClaudeCode } from '../dist/index.js';
15
+ // NOTE: Migrating to Claude Agent SDK:
16
+ // - System prompt is not applied by default
17
+ // - Filesystem settings (CLAUDE.md, settings.json) are not loaded by default
18
+ // To restore old behavior, set when creating model instances, e.g.:
19
+ // systemPrompt: { type: 'preset', preset: 'claude_code' }
20
+ // settingSources: ['user', 'project', 'local']
21
+
22
+ async function testToolManagement() {
23
+ console.log('šŸ”§ Testing Claude Code Tool Management\n');
24
+
25
+ // 1. Default behavior - all tools allowed
26
+ console.log('1ļøāƒ£ Default (all tools allowed)');
27
+ const defaultClaude = createClaudeCode();
28
+
29
+ try {
30
+ const result1 = streamText({
31
+ model: defaultClaude('opus'),
32
+ prompt: 'What is 2 + 2? Just give me the number.',
33
+ });
34
+
35
+ // Collect text from stream
36
+ let response1 = '';
37
+ for await (const chunk of result1.textStream) {
38
+ response1 += chunk;
39
+ }
40
+ console.log('Response:', response1.trim());
41
+ console.log(' (All built-in and MCP tools would be allowed)');
42
+ } catch (error) {
43
+ console.error('Error:', error);
44
+ }
45
+
46
+ console.log('\n2ļøāƒ£ Built-in tools: Allow only Bash commands');
47
+ // 2. Allow only specific Bash commands
48
+ const bashOnlyClaude = createClaudeCode({
49
+ defaultSettings: {
50
+ allowedTools: ['Bash(echo:*)', 'Bash(date)', 'Bash(pwd)'],
51
+ },
52
+ });
53
+
54
+ try {
55
+ const result2 = streamText({
56
+ model: bashOnlyClaude('opus'),
57
+ prompt: 'Can you show me the current date? Use the date command.',
58
+ });
59
+
60
+ // Collect text from stream
61
+ let response2 = '';
62
+ for await (const chunk of result2.textStream) {
63
+ response2 += chunk;
64
+ }
65
+ console.log('Response:', response2.trim());
66
+ console.log(' (Only allowed specific Bash commands)');
67
+ } catch (error) {
68
+ console.error('Error:', error);
69
+ }
70
+
71
+ console.log('\n3ļøāƒ£ Built-in tools: Block dangerous operations');
72
+ // 3. Block file modifications but allow reading
73
+ const readOnlyClaude = createClaudeCode({
74
+ defaultSettings: {
75
+ disallowedTools: ['Write', 'Edit', 'Delete', 'Bash(rm:*)', 'Bash(sudo:*)'],
76
+ },
77
+ });
78
+
79
+ try {
80
+ const result3 = streamText({
81
+ model: readOnlyClaude('opus'),
82
+ prompt: 'What is the capital of France? Just the city name.',
83
+ });
84
+
85
+ // Collect text from stream
86
+ let response3 = '';
87
+ for await (const chunk of result3.textStream) {
88
+ response3 += chunk;
89
+ }
90
+ console.log('Response:', response3.trim());
91
+ console.log(' (Can read files but not write/edit/delete)');
92
+ } catch (error) {
93
+ console.error('Error:', error);
94
+ }
95
+
96
+ console.log('\n4ļøāƒ£ Mixed: Built-in tools + MCP tools');
97
+ // 4. Allow specific built-in tools and MCP tools
98
+ const mixedClaude = createClaudeCode({
99
+ defaultSettings: {
100
+ allowedTools: [
101
+ 'Read',
102
+ 'LS',
103
+ 'Bash(git log:*)',
104
+ 'Bash(git status)',
105
+ 'mcp__filesystem__read_file',
106
+ 'mcp__git__status',
107
+ ],
108
+ },
109
+ });
110
+
111
+ try {
112
+ const result4 = streamText({
113
+ model: mixedClaude('opus'),
114
+ prompt: 'What is the result of 5 * 8?',
115
+ });
116
+
117
+ // Collect text from stream
118
+ let response4 = '';
119
+ for await (const chunk of result4.textStream) {
120
+ response4 += chunk;
121
+ }
122
+ console.log('Response:', response4.trim());
123
+ console.log(' (Only allowed read operations and git status)');
124
+ } catch (error) {
125
+ console.error('Error:', error);
126
+ }
127
+
128
+ console.log('\n5ļøāƒ£ Security lockdown: No tools at all');
129
+ // 5. Maximum security - explicit empty allowlist blocks all tools
130
+ const noToolsClaude = createClaudeCode({
131
+ defaultSettings: {
132
+ allowedTools: [], // Empty array = explicit empty allowlist = NO tools allowed
133
+ },
134
+ });
135
+
136
+ try {
137
+ const result5 = streamText({
138
+ model: noToolsClaude('opus'),
139
+ prompt: 'What programming language is this: console.log("Hello")?',
140
+ });
141
+
142
+ // Collect text from stream
143
+ let response5 = '';
144
+ for await (const chunk of result5.textStream) {
145
+ response5 += chunk;
146
+ }
147
+ console.log('Response:', response5.trim());
148
+ console.log(' (No tools allowed - explicit empty allowlist blocks everything)');
149
+ } catch (error) {
150
+ console.error('Error:', error);
151
+ }
152
+
153
+ console.log('\n6ļøāƒ£ Model-specific override');
154
+ // 6. Model-specific settings override provider settings
155
+ const baseClaude = createClaudeCode({
156
+ defaultSettings: {
157
+ disallowedTools: ['Bash', 'Write'], // Provider blocks these
158
+ },
159
+ });
160
+
161
+ try {
162
+ const result6 = streamText({
163
+ model: baseClaude('opus', {
164
+ // Override to allow everything for this specific call
165
+ disallowedTools: [],
166
+ }),
167
+ prompt: 'Name a popular web framework.',
168
+ });
169
+
170
+ // Collect text from stream
171
+ let response6 = '';
172
+ for await (const chunk of result6.textStream) {
173
+ response6 += chunk;
174
+ }
175
+ console.log('Response:', response6.trim());
176
+ console.log(' (Model override allows all tools for this call)');
177
+ } catch (error) {
178
+ console.error('Error:', error);
179
+ }
180
+
181
+ console.log('\nāœ… Tool management examples completed!');
182
+
183
+ console.log('\nšŸ“ Key Points:');
184
+ console.log('- Flags work for BOTH built-in tools AND MCP tools');
185
+ console.log('- Same rule syntax as settings.json permissions');
186
+ console.log('- Session-only overrides (higher priority than settings files)');
187
+ console.log('- Use specifiers for fine-grained control: Bash(git:*)');
188
+ console.log('- Empty allowedTools ([]) = Explicit empty allowlist = No tools allowed');
189
+ console.log('- Omitting flags entirely = Falls back to normal permission system');
190
+
191
+ console.log('\nšŸ› ļø Built-in tool names:');
192
+ console.log('- Bash, Edit, Read, Write, Delete, LS, Grep, Glob');
193
+ console.log('- WebFetch, NotebookRead, NotebookEdit');
194
+ console.log('- Use /permissions in Claude to see all available tools');
195
+
196
+ console.log('\nšŸ”Œ MCP tool format:');
197
+ console.log('- mcp__<serverName> (all tools from that server)');
198
+ console.log('- mcp__<serverName>__<toolName> (specific tool)');
199
+
200
+ console.log('\nšŸ”’ Security patterns:');
201
+ console.log('- Read-only: disallowedTools: ["Write", "Edit", "Delete"]');
202
+ console.log('- No shell: disallowedTools: ["Bash"]');
203
+ console.log('- Safe git: allowedTools: ["Bash(git log:*)", "Bash(git diff:*)"]');
204
+ }
205
+
206
+ // Run the examples
207
+ testToolManagement()
208
+ .then(() => {
209
+ console.log('\nAll examples completed successfully!');
210
+ process.exit(0);
211
+ })
212
+ .catch((error) => {
213
+ console.error('\nExample failed:', error);
214
+ process.exit(1);
215
+ });
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Example: Tool Streaming Events
3
+ *
4
+ * Streams a conversation that triggers Claude Code's built-in tools and prints the
5
+ * intermediate tool events emitted by the Vercel AI SDK integration.
6
+ *
7
+ * Requirements:
8
+ * - `npm run build` (so ../dist is up to date)
9
+ * - `claude login` and the CLI tools available on your PATH
10
+ * - Node.js ≄ 18
11
+ */
12
+
13
+ import { streamText } from 'ai';
14
+ import type { CanUseTool } from '@anthropic-ai/claude-agent-sdk';
15
+ import { claudeCode } from '../dist/index.js';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { dirname, resolve } from 'node:path';
18
+
19
+ const allowAllTools: CanUseTool = async (_toolName, input) => ({
20
+ behavior: 'allow',
21
+ updatedInput: input,
22
+ });
23
+
24
+ async function main() {
25
+ const exampleRoot = dirname(fileURLToPath(import.meta.url));
26
+ const workspaceRoot = resolve(exampleRoot, '..');
27
+ const readmePath = resolve(workspaceRoot, 'README.md');
28
+
29
+ const result = streamText({
30
+ model: claudeCode('opus', {
31
+ streamingInput: 'always',
32
+ canUseTool: allowAllTools,
33
+ permissionMode: 'bypassPermissions',
34
+ allowedTools: ['Bash', 'Read'],
35
+ cwd: workspaceRoot,
36
+ }),
37
+ prompt: [
38
+ {
39
+ role: 'user',
40
+ content: [
41
+ {
42
+ type: 'text',
43
+ text: `List the project directory and then read ${readmePath}. Summarize the findings once the tools finish.`,
44
+ },
45
+ ],
46
+ },
47
+ ],
48
+ });
49
+
50
+ console.log('Listening for tool and text events...\n');
51
+
52
+ const stream = result.fullStream as AsyncIterable<any>;
53
+
54
+ for await (const part of stream) {
55
+ switch (part.type) {
56
+ case 'start':
57
+ console.log('āš™ļø generation started');
58
+ break;
59
+ case 'start-step':
60
+ console.log('āž”ļø start-step', part.request ?? {});
61
+ break;
62
+ case 'stream-start':
63
+ console.log('⚔ stream-start');
64
+ if (Array.isArray(part.warnings) && part.warnings.length > 0) {
65
+ console.log(
66
+ ' warnings:',
67
+ part.warnings.map((warning: unknown) => JSON.stringify(warning))
68
+ );
69
+ }
70
+ break;
71
+ case 'response-metadata':
72
+ console.log(`ā„¹ļø session ${part.id ?? 'unknown'} (model ${part.modelId ?? 'unknown'})`);
73
+ break;
74
+ case 'tool-input-start':
75
+ console.log(`šŸ”§ tool-input-start → ${part.toolName} (${part.id})`);
76
+ break;
77
+ case 'tool-input-delta':
78
+ console.log(` ↳ input delta: ${part.delta}`);
79
+ break;
80
+ case 'tool-input-end':
81
+ console.log(` ↳ input end (${part.id})`);
82
+ break;
83
+ case 'tool-call':
84
+ console.log(`šŸš€ tool-call → ${part.toolName} (${part.toolCallId})`);
85
+ break;
86
+ case 'tool-error':
87
+ console.error('āš ļø tool-error:', part.toolName, part.error);
88
+ break;
89
+ case 'tool-result': {
90
+ console.log(`šŸ“„ tool-result ← ${part.toolName} (${part.toolCallId})`);
91
+ const toolResult = part.result ?? part.output;
92
+ if (toolResult !== undefined) {
93
+ console.dir(toolResult, { depth: 4 });
94
+ } else {
95
+ console.log(' (provider reported no structured result)');
96
+ }
97
+ break;
98
+ }
99
+ case 'text-start':
100
+ console.log('šŸ’¬ text-start');
101
+ break;
102
+ case 'text-delta':
103
+ {
104
+ const chunk = part.delta ?? part.text;
105
+ if (typeof chunk === 'string') {
106
+ process.stdout.write(chunk);
107
+ } else {
108
+ console.dir(chunk, { depth: 2 });
109
+ }
110
+ }
111
+ break;
112
+ case 'text-end':
113
+ console.log('\nšŸ’¬ text-end\n');
114
+ break;
115
+ case 'finish':
116
+ console.log('āœ… finish', part.finishReason);
117
+ console.log(' usage:', part.usage ?? part.totalUsage);
118
+ break;
119
+ case 'error':
120
+ console.error('āŒ error part:', part.error);
121
+ break;
122
+ default:
123
+ console.log('⋯ other part:', part);
124
+ break;
125
+ }
126
+ }
127
+ }
128
+
129
+ main().catch((error) => {
130
+ console.error('Example failed:', error);
131
+ process.exitCode = 1;
132
+ });
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ /**
4
+ * Zod 4 Compatibility Test
5
+ *
6
+ * This example demonstrates that @boostecom/provider works correctly
7
+ * with Zod v4.x. It tests various Zod 4 features and schema patterns.
8
+ *
9
+ * Changes in Zod 4:
10
+ * - Function schemas: z.function().args().returns() -> z.function({ input, output })
11
+ * - Better type inference
12
+ * - Improved error messages
13
+ * - New validation features
14
+ */
15
+
16
+ import { createClaudeCode } from '../dist/index.js';
17
+ import { generateObject, streamText } from 'ai';
18
+ import { z } from 'zod';
19
+
20
+ const claudeCode = createClaudeCode();
21
+
22
+ console.log('🧪 Testing Zod 4 Compatibility with @boostecom/provider\n');
23
+
24
+ // Check Zod version
25
+ async function checkZodVersion() {
26
+ try {
27
+ const zodPkg = await import('zod/package.json', { with: { type: 'json' } });
28
+ console.log(`šŸ“¦ Zod version: ${zodPkg.default.version}\n`);
29
+ } catch {
30
+ console.log('šŸ“¦ Zod version: 4.x (unable to read package.json)\n');
31
+ }
32
+ }
33
+
34
+ // Test 1: Basic Zod 4 schemas
35
+ async function test1_basicSchemas() {
36
+ console.log('1ļøāƒ£ Basic Zod 4 Schemas\n');
37
+
38
+ const schema = z.object({
39
+ // Primitives
40
+ name: z.string(),
41
+ age: z.number(),
42
+ isActive: z.boolean(),
43
+ // Optional
44
+ nickname: z.string().optional(),
45
+ // Arrays
46
+ tags: z.array(z.string()),
47
+ // Enums
48
+ role: z.enum(['admin', 'user', 'guest']),
49
+ // Dates
50
+ createdAt: z.string().datetime(),
51
+ });
52
+
53
+ const { object } = await generateObject({
54
+ model: claudeCode('opus'),
55
+ schema,
56
+ prompt: 'Generate a user profile for a software developer',
57
+ });
58
+
59
+ console.log('Generated object:');
60
+ console.log(JSON.stringify(object, null, 2));
61
+ console.log('āœ… Basic schemas work!\n');
62
+ }
63
+
64
+ // Test 2: Zod 4 function schemas (NEW API)
65
+ async function test2_functionSchemas() {
66
+ console.log('2ļøāƒ£ Zod 4 Function Schemas (New API)\n');
67
+
68
+ // In Zod 4, function schemas use the new API:
69
+ // z.function({ input, output }) instead of z.function().args().returns()
70
+ // Using Zod 4 function schema API
71
+ const callbackSchema = z.function({
72
+ input: z.tuple([z.string()]),
73
+ output: z.void(),
74
+ });
75
+
76
+ console.log('Function schema created with new Zod 4 API:');
77
+ console.log('z.function({ input: z.tuple([z.string()]), output: z.void() })');
78
+
79
+ // Test the schema
80
+ const testFn = (msg: string) => console.log(msg);
81
+ const result = callbackSchema.safeParse(testFn);
82
+
83
+ if (result.success) {
84
+ console.log('āœ… Function schema validation passed!\n');
85
+ } else {
86
+ console.log('āŒ Function schema validation failed\n');
87
+ }
88
+ }
89
+
90
+ // Test 3: Complex nested objects
91
+ async function test3_nestedObjects() {
92
+ console.log('3ļøāƒ£ Complex Nested Objects\n');
93
+
94
+ const schema = z.object({
95
+ user: z.object({
96
+ id: z.string(),
97
+ profile: z.object({
98
+ firstName: z.string(),
99
+ lastName: z.string(),
100
+ contact: z.object({
101
+ email: z.string().email(),
102
+ phone: z.string().optional(),
103
+ }),
104
+ }),
105
+ }),
106
+ settings: z.object({
107
+ theme: z.enum(['light', 'dark', 'auto']),
108
+ notifications: z.object({
109
+ email: z.boolean(),
110
+ push: z.boolean(),
111
+ sms: z.boolean(),
112
+ }),
113
+ }),
114
+ });
115
+
116
+ const { object } = await generateObject({
117
+ model: claudeCode('opus'),
118
+ schema,
119
+ prompt: 'Generate a complete user profile with settings',
120
+ });
121
+
122
+ console.log('Generated nested object:');
123
+ console.log(JSON.stringify(object, null, 2));
124
+ console.log('āœ… Nested objects work!\n');
125
+ }
126
+
127
+ // Test 4: Arrays and unions
128
+ async function test4_arraysAndUnions() {
129
+ console.log('4ļøāƒ£ Arrays and Unions\n');
130
+
131
+ const schema = z.object({
132
+ tasks: z.array(
133
+ z.object({
134
+ id: z.string(),
135
+ title: z.string(),
136
+ status: z.union([z.literal('todo'), z.literal('in-progress'), z.literal('done')]),
137
+ priority: z.number().min(1).max(5),
138
+ assignee: z
139
+ .object({
140
+ name: z.string(),
141
+ email: z.string().email(),
142
+ })
143
+ .optional(),
144
+ })
145
+ ),
146
+ });
147
+
148
+ const { object } = await generateObject({
149
+ model: claudeCode('opus'),
150
+ schema,
151
+ prompt: 'Generate 3 tasks for a software project',
152
+ });
153
+
154
+ console.log('Generated tasks:');
155
+ console.log(JSON.stringify(object, null, 2));
156
+ console.log('āœ… Arrays and unions work!\n');
157
+ }
158
+
159
+ // Test 5: String validations
160
+ async function test5_stringValidations() {
161
+ console.log('5ļøāƒ£ String Validations\n');
162
+
163
+ const schema = z.object({
164
+ email: z.string().email(),
165
+ url: z.string().url(),
166
+ uuid: z.string().uuid(),
167
+ username: z.string().min(3).max(20),
168
+ description: z.string().max(200),
169
+ });
170
+
171
+ const { object } = await generateObject({
172
+ model: claudeCode('opus'),
173
+ schema,
174
+ prompt: 'Generate a valid user registration with all fields validated',
175
+ });
176
+
177
+ console.log('Generated validated object:');
178
+ console.log(JSON.stringify(object, null, 2));
179
+ console.log('āœ… String validations work!\n');
180
+ }
181
+
182
+ // Test 6: Number validations
183
+ async function test6_numberValidations() {
184
+ console.log('6ļøāƒ£ Number Validations\n');
185
+
186
+ const schema = z.object({
187
+ age: z.number().int().min(0).max(120),
188
+ price: z.number().positive(),
189
+ discount: z.number().min(0).max(1),
190
+ rating: z.number().int().min(1).max(5),
191
+ temperature: z.number().min(-273.15), // Absolute zero
192
+ });
193
+
194
+ const { object } = await generateObject({
195
+ model: claudeCode('opus'),
196
+ schema,
197
+ prompt: 'Generate a product with age restriction, price, discount, rating, and temperature',
198
+ });
199
+
200
+ console.log('Generated validated numbers:');
201
+ console.log(JSON.stringify(object, null, 2));
202
+ console.log('āœ… Number validations work!\n');
203
+ }
204
+
205
+ // Test 7: Streaming with Zod 4
206
+ async function test7_streaming() {
207
+ console.log('7ļøāƒ£ Streaming with Zod 4 (text mode)\n');
208
+
209
+ const result = streamText({
210
+ model: claudeCode('opus'),
211
+ prompt: 'Count from 1 to 5, one number per line',
212
+ });
213
+
214
+ console.log('Streaming response:');
215
+ for await (const chunk of result.textStream) {
216
+ process.stdout.write(chunk);
217
+ }
218
+
219
+ console.log('\nāœ… Streaming works!\n');
220
+ }
221
+
222
+ // Test 8: Discriminated unions
223
+ async function test8_discriminatedUnions() {
224
+ console.log('8ļøāƒ£ Discriminated Unions\n');
225
+
226
+ const schema = z.object({
227
+ events: z.array(
228
+ z.discriminatedUnion('type', [
229
+ z.object({
230
+ type: z.literal('login'),
231
+ userId: z.string(),
232
+ timestamp: z.string(),
233
+ }),
234
+ z.object({
235
+ type: z.literal('purchase'),
236
+ userId: z.string(),
237
+ amount: z.number(),
238
+ items: z.array(z.string()),
239
+ }),
240
+ z.object({
241
+ type: z.literal('logout'),
242
+ userId: z.string(),
243
+ sessionDuration: z.number(),
244
+ }),
245
+ ])
246
+ ),
247
+ });
248
+
249
+ const { object } = await generateObject({
250
+ model: claudeCode('opus'),
251
+ schema,
252
+ prompt: 'Generate 3 different user events: login, purchase, and logout',
253
+ });
254
+
255
+ console.log('Generated discriminated unions:');
256
+ console.log(JSON.stringify(object, null, 2));
257
+ console.log('āœ… Discriminated unions work!\n');
258
+ }
259
+
260
+ // Main execution
261
+ async function main() {
262
+ try {
263
+ await checkZodVersion();
264
+ await test1_basicSchemas();
265
+ await test2_functionSchemas();
266
+ await test3_nestedObjects();
267
+ await test4_arraysAndUnions();
268
+ await test5_stringValidations();
269
+ await test6_numberValidations();
270
+ await test7_streaming();
271
+ await test8_discriminatedUnions();
272
+
273
+ console.log('šŸŽ‰ All Zod 4 compatibility tests passed!\n');
274
+ console.log('āœ… @boostecom/provider is fully compatible with Zod 4\n');
275
+ console.log('šŸ“ Key changes in Zod 4:');
276
+ console.log(
277
+ ' - Function schemas: z.function({ input: [...], output: ... }) instead of .args().returns()'
278
+ );
279
+ console.log(' - Better TypeScript inference');
280
+ console.log(' - Improved error messages');
281
+ console.log(' - All other Zod features work as expected\n');
282
+ } catch (error) {
283
+ console.error('āŒ Error:', error);
284
+ console.log('\nšŸ’” Make sure you have Zod 4.x installed:');
285
+ console.log(' npm install zod@^4.0.0');
286
+ process.exit(1);
287
+ }
288
+ }
289
+
290
+ main().catch(console.error);