@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.
- package/README.md +90 -0
- package/dist/index.cjs +2522 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +848 -0
- package/dist/index.d.ts +848 -0
- package/dist/index.js +2484 -0
- package/dist/index.js.map +1 -0
- package/docs/content/README.md +337 -0
- package/docs/content/agent-teams.mdx +324 -0
- package/docs/content/api.mdx +757 -0
- package/docs/content/best-practices.mdx +624 -0
- package/docs/content/examples.mdx +675 -0
- package/docs/content/guide.mdx +516 -0
- package/docs/content/index.mdx +99 -0
- package/docs/content/installation.mdx +246 -0
- package/docs/content/skills.mdx +548 -0
- package/docs/content/troubleshooting.mdx +588 -0
- package/docs/examples/README.md +499 -0
- package/docs/examples/abort-signal.ts +125 -0
- package/docs/examples/agent-teams.ts +122 -0
- package/docs/examples/basic-usage.ts +73 -0
- package/docs/examples/check-cli.ts +51 -0
- package/docs/examples/conversation-history.ts +69 -0
- package/docs/examples/custom-config.ts +90 -0
- package/docs/examples/generate-object-constraints.ts +209 -0
- package/docs/examples/generate-object.ts +211 -0
- package/docs/examples/hooks-callbacks.ts +63 -0
- package/docs/examples/images.ts +76 -0
- package/docs/examples/integration-test.ts +241 -0
- package/docs/examples/limitations.ts +150 -0
- package/docs/examples/logging-custom-logger.ts +99 -0
- package/docs/examples/logging-default.ts +55 -0
- package/docs/examples/logging-disabled.ts +74 -0
- package/docs/examples/logging-verbose.ts +64 -0
- package/docs/examples/long-running-tasks.ts +179 -0
- package/docs/examples/message-injection.ts +210 -0
- package/docs/examples/mid-stream-injection.ts +126 -0
- package/docs/examples/run-all-examples.sh +48 -0
- package/docs/examples/sdk-tools-callbacks.ts +49 -0
- package/docs/examples/skills-discovery.ts +144 -0
- package/docs/examples/skills-management.ts +140 -0
- package/docs/examples/stream-object.ts +80 -0
- package/docs/examples/streaming.ts +52 -0
- package/docs/examples/structured-output-repro.ts +227 -0
- package/docs/examples/tool-management.ts +215 -0
- package/docs/examples/tool-streaming.ts +132 -0
- package/docs/examples/zod4-compatibility-test.ts +290 -0
- package/docs/src/claude-code-language-model.test.ts +3883 -0
- package/docs/src/claude-code-language-model.ts +2586 -0
- package/docs/src/claude-code-provider.test.ts +97 -0
- package/docs/src/claude-code-provider.ts +179 -0
- package/docs/src/convert-to-claude-code-messages.images.test.ts +104 -0
- package/docs/src/convert-to-claude-code-messages.test.ts +193 -0
- package/docs/src/convert-to-claude-code-messages.ts +419 -0
- package/docs/src/errors.test.ts +213 -0
- package/docs/src/errors.ts +216 -0
- package/docs/src/index.test.ts +49 -0
- package/docs/src/index.ts +98 -0
- package/docs/src/logger.integration.test.ts +164 -0
- package/docs/src/logger.test.ts +184 -0
- package/docs/src/logger.ts +65 -0
- package/docs/src/map-claude-code-finish-reason.test.ts +120 -0
- package/docs/src/map-claude-code-finish-reason.ts +60 -0
- package/docs/src/mcp-helpers.test.ts +71 -0
- package/docs/src/mcp-helpers.ts +123 -0
- package/docs/src/message-injection.test.ts +460 -0
- package/docs/src/types.ts +447 -0
- package/docs/src/validation.test.ts +558 -0
- package/docs/src/validation.ts +360 -0
- 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);
|