@lantos1618/better-ui 0.2.2 → 0.3.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 (59) hide show
  1. package/README.md +231 -148
  2. package/dist/index.d.mts +314 -0
  3. package/dist/index.d.ts +314 -0
  4. package/dist/index.js +522 -0
  5. package/dist/index.mjs +491 -0
  6. package/package.json +59 -20
  7. package/lib/aui/README.md +0 -136
  8. package/lib/aui/__tests__/aui-complete.test.ts +0 -251
  9. package/lib/aui/__tests__/aui-comprehensive.test.ts +0 -376
  10. package/lib/aui/__tests__/aui-concise.test.ts +0 -278
  11. package/lib/aui/__tests__/aui-integration.test.ts +0 -309
  12. package/lib/aui/__tests__/aui-simple.test.ts +0 -116
  13. package/lib/aui/__tests__/aui.test.ts +0 -269
  14. package/lib/aui/__tests__/concise-api.test.ts +0 -165
  15. package/lib/aui/__tests__/core.test.ts +0 -265
  16. package/lib/aui/__tests__/simple-api.test.ts +0 -200
  17. package/lib/aui/ai-assistant.ts +0 -408
  18. package/lib/aui/ai-control.ts +0 -353
  19. package/lib/aui/client/use-aui.ts +0 -55
  20. package/lib/aui/client-control.ts +0 -551
  21. package/lib/aui/client-executor.ts +0 -417
  22. package/lib/aui/components/ToolRenderer.tsx +0 -22
  23. package/lib/aui/core.ts +0 -137
  24. package/lib/aui/demo.tsx +0 -89
  25. package/lib/aui/examples/ai-complete-demo.tsx +0 -359
  26. package/lib/aui/examples/ai-control-demo.tsx +0 -356
  27. package/lib/aui/examples/ai-control-tools.ts +0 -308
  28. package/lib/aui/examples/concise-api.tsx +0 -153
  29. package/lib/aui/examples/index.tsx +0 -163
  30. package/lib/aui/examples/quick-demo.tsx +0 -91
  31. package/lib/aui/examples/simple-demo.tsx +0 -71
  32. package/lib/aui/examples/simple-tools.tsx +0 -160
  33. package/lib/aui/examples/user-api.tsx +0 -208
  34. package/lib/aui/examples/user-requested.tsx +0 -174
  35. package/lib/aui/examples/weather-search-tools.tsx +0 -119
  36. package/lib/aui/examples.tsx +0 -367
  37. package/lib/aui/hooks/useAUITool.ts +0 -142
  38. package/lib/aui/hooks/useAUIToolEnhanced.ts +0 -343
  39. package/lib/aui/hooks/useAUITools.ts +0 -195
  40. package/lib/aui/index.ts +0 -156
  41. package/lib/aui/provider.tsx +0 -45
  42. package/lib/aui/server-control.ts +0 -386
  43. package/lib/aui/server-executor.ts +0 -165
  44. package/lib/aui/server.ts +0 -167
  45. package/lib/aui/tool-registry.ts +0 -380
  46. package/lib/aui/tools/advanced-examples.tsx +0 -86
  47. package/lib/aui/tools/ai-complete.ts +0 -375
  48. package/lib/aui/tools/api-tools.tsx +0 -230
  49. package/lib/aui/tools/data-tools.tsx +0 -232
  50. package/lib/aui/tools/dom-tools.tsx +0 -202
  51. package/lib/aui/tools/examples.ts +0 -43
  52. package/lib/aui/tools/file-tools.tsx +0 -202
  53. package/lib/aui/tools/form-tools.tsx +0 -233
  54. package/lib/aui/tools/index.ts +0 -8
  55. package/lib/aui/tools/navigation-tools.tsx +0 -172
  56. package/lib/aui/tools/notification-tools.ts +0 -213
  57. package/lib/aui/tools/state-tools.tsx +0 -209
  58. package/lib/aui/types.ts +0 -47
  59. package/lib/aui/vercel-ai.ts +0 -100
@@ -1,165 +0,0 @@
1
- import aui from '../index';
2
- import { z } from 'zod';
3
-
4
- describe('AUI Concise API Tests', () => {
5
- beforeEach(() => {
6
- aui.clear();
7
- });
8
-
9
- describe('Simple Tool Pattern', () => {
10
- it('should create tool with just 2 methods (input + execute)', async () => {
11
- const simpleTool = aui
12
- .tool('weather')
13
- .input(z.object({ city: z.string() }))
14
- .execute(async ({ input }) => ({ temp: 72, city: input.city }));
15
-
16
- const result = await simpleTool.run({ city: 'SF' });
17
- expect(result).toEqual({ temp: 72, city: 'SF' });
18
- });
19
-
20
- it('should work with render method', async () => {
21
- const weatherTool = aui
22
- .tool('weather')
23
- .input(z.object({ city: z.string() }))
24
- .execute(async ({ input }) => ({ temp: 72, city: input.city }))
25
- .render(({ data }) => ({ type: 'div', props: { children: `${data.city}: ${data.temp}°` } }) as any);
26
-
27
- const result = await weatherTool.run({ city: 'NYC' });
28
- expect(result).toEqual({ temp: 72, city: 'NYC' });
29
- expect(weatherTool.renderer).toBeDefined();
30
- });
31
- });
32
-
33
- describe('Complex Tool Pattern', () => {
34
- it('should support client optimization', async () => {
35
- const searchTool = aui
36
- .tool('search')
37
- .input(z.object({ query: z.string() }))
38
- .execute(async ({ input }) => ({ results: [`Server: ${input.query}`] }))
39
- .clientExecute(async ({ input, ctx }) => {
40
- const cached = ctx.cache.get(input.query);
41
- if (cached) return cached;
42
-
43
- const result = { results: [`Client: ${input.query}`] };
44
- ctx.cache.set(input.query, result);
45
- return result;
46
- });
47
-
48
- // Server context (isServer: true)
49
- const serverResult = await searchTool.run(
50
- { query: 'test' },
51
- { isServer: true, cache: new Map(), fetch: global.fetch }
52
- );
53
- expect(serverResult.results[0]).toBe('Server: test');
54
-
55
- // Client context (isServer: false)
56
- const clientCache = new Map();
57
- const clientResult = await searchTool.run(
58
- { query: 'test' },
59
- { isServer: false, cache: clientCache, fetch: global.fetch }
60
- );
61
- expect(clientResult.results[0]).toBe('Client: test');
62
-
63
- // Check caching works
64
- const cachedResult = await searchTool.run(
65
- { query: 'test' },
66
- { isServer: false, cache: clientCache, fetch: global.fetch }
67
- );
68
- expect(cachedResult).toBe(clientResult); // Same reference = cached
69
- });
70
- });
71
-
72
- describe('Frontend/Backend Control', () => {
73
- it('should allow database queries (backend)', async () => {
74
- const dbTool = aui
75
- .tool('queryDB')
76
- .input(z.object({ table: z.string(), limit: z.number() }))
77
- .execute(async ({ input }) => ({
78
- rows: Array(input.limit).fill(null).map((_, i) => ({
79
- id: i + 1,
80
- table: input.table
81
- })),
82
- count: input.limit
83
- }));
84
-
85
- const result = await dbTool.run({ table: 'users', limit: 3 });
86
- expect(result.count).toBe(3);
87
- expect(result.rows).toHaveLength(3);
88
- expect(result.rows[0].table).toBe('users');
89
- });
90
-
91
- it('should allow DOM manipulation (frontend)', async () => {
92
- const domTool = aui
93
- .tool('updateDOM')
94
- .input(z.object({ selector: z.string(), text: z.string() }))
95
- .clientExecute(async ({ input }) => {
96
- // Mock DOM operation
97
- return {
98
- success: true,
99
- selector: input.selector,
100
- text: input.text,
101
- isClient: true
102
- };
103
- });
104
-
105
- const result = await domTool.run(
106
- { selector: '#title', text: 'New Title' },
107
- { isServer: false, cache: new Map(), fetch: global.fetch }
108
- );
109
-
110
- expect(result.success).toBe(true);
111
- expect(result.isClient).toBe(true);
112
- });
113
-
114
- it('should handle API calls', async () => {
115
- const apiTool = aui
116
- .tool('callAPI')
117
- .input(z.object({ endpoint: z.string(), method: z.string() }))
118
- .execute(async ({ input }) => ({
119
- status: 200,
120
- endpoint: input.endpoint,
121
- method: input.method,
122
- data: { message: 'Success' }
123
- }));
124
-
125
- const result = await apiTool.run({ endpoint: '/api/users', method: 'GET' });
126
- expect(result.status).toBe(200);
127
- expect(result.data.message).toBe('Success');
128
- });
129
- });
130
-
131
- describe('Chaining and Fluent Interface', () => {
132
- it('should support method chaining without build()', async () => {
133
- const tool = aui
134
- .tool('chain-test')
135
- .input(z.object({ value: z.number() }))
136
- .execute(async ({ input }) => ({ doubled: input.value * 2 }))
137
- .describe('Doubles a number')
138
- .tag('math', 'simple')
139
- .middleware(async ({ input, next }) => {
140
- // Log before execution
141
- const result = await next();
142
- // Log after execution
143
- return result;
144
- });
145
-
146
- const result = await tool.run({ value: 5 });
147
- expect(result.doubled).toBe(10);
148
- expect(tool.description).toBe('Doubles a number');
149
- expect(tool.tags).toContain('math');
150
- expect(tool.tags).toContain('simple');
151
- });
152
-
153
- it('should return tool directly without build step', () => {
154
- const tool = aui
155
- .tool('direct')
156
- .input(z.object({ test: z.string() }))
157
- .execute(async ({ input }) => input);
158
-
159
- // Tool is immediately usable
160
- expect(tool.name).toBe('direct');
161
- expect(tool.run).toBeDefined();
162
- expect(typeof tool.run).toBe('function');
163
- });
164
- });
165
- });
@@ -1,265 +0,0 @@
1
- import { describe, it, expect, beforeEach } from '@jest/globals';
2
- import aui, { AUI, z } from '../index';
3
-
4
- describe('AUI Core System', () => {
5
- let testAUI: AUI;
6
-
7
- beforeEach(() => {
8
- testAUI = new AUI();
9
- });
10
-
11
- describe('Simple Tool Pattern', () => {
12
- it('should create a simple tool with just execute and render', () => {
13
- const weatherTool = testAUI
14
- .tool('weather')
15
- .input(z.object({ city: z.string() }))
16
- .execute(async ({ input }) => ({ temp: 72, city: input.city }));
17
-
18
- expect(weatherTool.name).toBe('weather');
19
- expect(weatherTool.schema).toBeDefined();
20
- });
21
-
22
- it('should execute simple tool correctly', async () => {
23
- const weatherTool = testAUI
24
- .tool('weather')
25
- .input(z.object({ city: z.string() }))
26
- .execute(async ({ input }) => ({ temp: 72, city: input.city }));
27
-
28
- const result = await weatherTool.run({ city: 'San Francisco' });
29
- expect(result).toEqual({ temp: 72, city: 'San Francisco' });
30
- });
31
-
32
- it('should validate input with zod schema', async () => {
33
- const tool = testAUI
34
- .tool('validated')
35
- .input(z.object({
36
- email: z.string().email(),
37
- age: z.number().min(18)
38
- }))
39
- .execute(async ({ input }) => ({ valid: true, ...input }));
40
-
41
- await expect(tool.run({ email: 'invalid', age: 17 }))
42
- .rejects.toThrow();
43
-
44
- const result = await tool.run({ email: 'test@example.com', age: 25 });
45
- expect(result).toEqual({ valid: true, email: 'test@example.com', age: 25 });
46
- });
47
- });
48
-
49
- describe('Complex Tool Pattern', () => {
50
- it('should create tool with client execution', () => {
51
- const searchTool = testAUI
52
- .tool('search')
53
- .input(z.object({ query: z.string() }))
54
- .execute(async ({ input }) => [{ id: 1, title: input.query }])
55
- .clientExecute(async ({ input, ctx }) => {
56
- const cached = ctx.cache.get(input.query);
57
- return cached || [{ id: 2, title: `Client: ${input.query}` }];
58
- });
59
-
60
- expect(searchTool.name).toBe('search');
61
- const config = searchTool.getConfig();
62
- expect(config.clientHandler).toBeDefined();
63
- });
64
-
65
- it('should use client handler when not on server', async () => {
66
- const tool = testAUI
67
- .tool('hybrid')
68
- .input(z.object({ value: z.string() }))
69
- .execute(async ({ input }) => ({ source: 'server', value: input.value }))
70
- .clientExecute(async ({ input }) => ({ source: 'client', value: input.value }));
71
-
72
- // Test with client context
73
- const clientResult = await tool.run(
74
- { value: 'test' },
75
- { cache: new Map(), fetch, isServer: false }
76
- );
77
- expect(clientResult).toEqual({ source: 'client', value: 'test' });
78
-
79
- // Test with server context
80
- const serverResult = await tool.run(
81
- { value: 'test' },
82
- { cache: new Map(), fetch, isServer: true }
83
- );
84
- expect(serverResult).toEqual({ source: 'server', value: 'test' });
85
- });
86
-
87
- it('should use cache in client execution', async () => {
88
- const cache = new Map();
89
- cache.set('cached-query', [{ id: 99, title: 'Cached Result' }]);
90
-
91
- const tool = testAUI
92
- .tool('search')
93
- .input(z.object({ query: z.string() }))
94
- .execute(async ({ input }) => [{ id: 1, title: input.query }])
95
- .clientExecute(async ({ input, ctx }) => {
96
- const cached = ctx.cache.get(input.query);
97
- if (cached) return cached;
98
- return [{ id: 2, title: `Fresh: ${input.query}` }];
99
- });
100
-
101
- const result = await tool.run(
102
- { query: 'cached-query' },
103
- { cache, fetch, isServer: false }
104
- );
105
-
106
- expect(result).toEqual([{ id: 99, title: 'Cached Result' }]);
107
- });
108
- });
109
-
110
- describe('Middleware Support', () => {
111
- it('should apply middleware in order', async () => {
112
- const logs: string[] = [];
113
-
114
- const tool = testAUI
115
- .tool('middleware-test')
116
- .input(z.object({ value: z.number() }))
117
- .execute(async ({ input }) => ({ result: input.value * 2 }))
118
- .middleware(async ({ input, next }) => {
119
- logs.push('middleware-1-before');
120
- const result = await next();
121
- logs.push('middleware-1-after');
122
- return { ...result, middleware1: true };
123
- })
124
- .middleware(async ({ input, next }) => {
125
- logs.push('middleware-2-before');
126
- const result = await next();
127
- logs.push('middleware-2-after');
128
- return { ...result, middleware2: true };
129
- });
130
-
131
- const result = await tool.run({ value: 5 });
132
-
133
- expect(logs).toEqual([
134
- 'middleware-1-before',
135
- 'middleware-2-before',
136
- 'middleware-2-after',
137
- 'middleware-1-after'
138
- ]);
139
-
140
- expect(result).toEqual({
141
- result: 10,
142
- middleware1: true,
143
- middleware2: true
144
- });
145
- });
146
- });
147
-
148
- describe('Tool Management', () => {
149
- it('should register and retrieve tools', () => {
150
- const tool1 = testAUI.tool('tool1').execute(async () => 'result1');
151
- const tool2 = testAUI.tool('tool2').execute(async () => 'result2');
152
-
153
- expect(testAUI.has('tool1')).toBe(true);
154
- expect(testAUI.has('tool2')).toBe(true);
155
- expect(testAUI.get('tool1')).toBe(tool1);
156
- expect(testAUI.get('tool2')).toBe(tool2);
157
- });
158
-
159
- it('should list all tools', () => {
160
- testAUI.tool('a').execute(async () => 'a');
161
- testAUI.tool('b').execute(async () => 'b');
162
- testAUI.tool('c').execute(async () => 'c');
163
-
164
- const tools = testAUI.list();
165
- expect(tools).toHaveLength(3);
166
- expect(testAUI.getToolNames()).toEqual(['a', 'b', 'c']);
167
- });
168
-
169
- it('should execute tool by name', async () => {
170
- testAUI
171
- .tool('calculator')
172
- .input(z.object({ a: z.number(), b: z.number() }))
173
- .execute(async ({ input }) => input.a + input.b);
174
-
175
- const result = await testAUI.execute('calculator', { a: 5, b: 3 });
176
- expect(result).toBe(8);
177
- });
178
-
179
- it('should find tools by tags', () => {
180
- testAUI.tool('t1').tag('ai', 'search').execute(async () => 't1');
181
- testAUI.tool('t2').tag('ai', 'database').execute(async () => 't2');
182
- testAUI.tool('t3').tag('search').execute(async () => 't3');
183
-
184
- const aiTools = testAUI.findByTag('ai');
185
- expect(aiTools).toHaveLength(2);
186
-
187
- const searchTools = testAUI.findByTag('search');
188
- expect(searchTools).toHaveLength(2);
189
-
190
- const aiSearchTools = testAUI.findByTags('ai', 'search');
191
- expect(aiSearchTools).toHaveLength(1);
192
- expect(aiSearchTools[0].name).toBe('t1');
193
- });
194
- });
195
-
196
- describe('Tool Metadata', () => {
197
- it('should support descriptions', () => {
198
- const tool = testAUI
199
- .tool('described')
200
- .describe('This tool does something important')
201
- .execute(async () => 'result');
202
-
203
- expect(tool.description).toBe('This tool does something important');
204
- });
205
-
206
- it('should serialize to JSON', () => {
207
- const tool = testAUI
208
- .tool('serializable')
209
- .describe('Test tool')
210
- .tag('test', 'example')
211
- .input(z.object({ x: z.number() }))
212
- .execute(async ({ input }) => input.x * 2)
213
- .clientExecute(async ({ input }) => input.x * 3)
214
- .middleware(async ({ next }) => next());
215
-
216
- const json = tool.toJSON();
217
- expect(json).toEqual({
218
- name: 'serializable',
219
- description: 'Test tool',
220
- tags: ['test', 'example'],
221
- hasInput: true,
222
- hasExecute: true,
223
- hasClientExecute: true,
224
- hasRender: false,
225
- hasMiddleware: true
226
- });
227
- });
228
- });
229
-
230
- describe('Error Handling', () => {
231
- it('should throw error for tool without execute handler', async () => {
232
- const tool = testAUI
233
- .tool('incomplete')
234
- .input(z.object({ x: z.number() }));
235
-
236
- await expect(tool.run({ x: 5 }))
237
- .rejects.toThrow('Tool incomplete has no execute handler');
238
- });
239
-
240
- it('should throw error for unknown tool', async () => {
241
- await expect(testAUI.execute('nonexistent', {}))
242
- .rejects.toThrow('Tool "nonexistent" not found');
243
- });
244
- });
245
-
246
- describe('Context Creation', () => {
247
- it('should create default context', () => {
248
- const ctx = testAUI.createContext();
249
- expect(ctx.cache).toBeInstanceOf(Map);
250
- expect(ctx.fetch).toBeDefined();
251
- expect(typeof ctx.isServer).toBe('boolean');
252
- });
253
-
254
- it('should merge additional context', () => {
255
- const ctx = testAUI.createContext({
256
- user: { id: 1, name: 'Test User' },
257
- session: { token: 'abc123' }
258
- });
259
-
260
- expect(ctx.user).toEqual({ id: 1, name: 'Test User' });
261
- expect(ctx.session).toEqual({ token: 'abc123' });
262
- expect(ctx.cache).toBeInstanceOf(Map);
263
- });
264
- });
265
- });
@@ -1,200 +0,0 @@
1
- import { describe, it, expect, beforeEach } from '@jest/globals';
2
- import { AUI } from '../index';
3
- import { z } from 'zod';
4
-
5
- describe('AUI Simple API', () => {
6
- let aui: AUI;
7
-
8
- beforeEach(() => {
9
- aui = new AUI();
10
- });
11
-
12
- describe('Basic Tool Creation', () => {
13
- it('should create a simple tool with just execute', async () => {
14
- const tool = aui
15
- .tool('simple')
16
- .execute(async () => ({ result: 'success' }));
17
-
18
- const result = await tool.run({});
19
- expect(result).toEqual({ result: 'success' });
20
- });
21
-
22
- it('should create a tool with input validation', async () => {
23
- const tool = aui
24
- .tool('validated')
25
- .input(z.object({ name: z.string() }))
26
- .execute(async ({ input }) => ({ greeting: `Hello, ${input.name}!` }));
27
-
28
- const result = await tool.run({ name: 'World' });
29
- expect(result).toEqual({ greeting: 'Hello, World!' });
30
- });
31
-
32
- it('should throw on invalid input', async () => {
33
- const tool = aui
34
- .tool('strict')
35
- .input(z.object({ age: z.number().min(0) }))
36
- .execute(async ({ input }) => ({ valid: true, age: input.age }));
37
-
38
- await expect(tool.run({ age: -1 } as any)).rejects.toThrow();
39
- });
40
- });
41
-
42
- describe('Client/Server Execution', () => {
43
- it('should use server execute when on server', async () => {
44
- const tool = aui
45
- .tool('dual')
46
- .execute(async () => ({ from: 'server' }))
47
- .clientExecute(async () => ({ from: 'client' }));
48
-
49
- const serverCtx = aui.createContext({ isServer: true });
50
- const result = await tool.run({}, serverCtx);
51
- expect(result).toEqual({ from: 'server' });
52
- });
53
-
54
- it('should use client execute when on client', async () => {
55
- const tool = aui
56
- .tool('dual')
57
- .execute(async () => ({ from: 'server' }))
58
- .clientExecute(async () => ({ from: 'client' }));
59
-
60
- const clientCtx = aui.createContext({ isServer: false });
61
- const result = await tool.run({}, clientCtx);
62
- expect(result).toEqual({ from: 'client' });
63
- });
64
- });
65
-
66
- describe('Middleware', () => {
67
- it('should apply middleware in order', async () => {
68
- const log: string[] = [];
69
-
70
- const tool = aui
71
- .tool('middleware')
72
- .middleware(async ({ next }) => {
73
- log.push('before-1');
74
- const result = await next();
75
- log.push('after-1');
76
- return result;
77
- })
78
- .middleware(async ({ next }) => {
79
- log.push('before-2');
80
- const result = await next();
81
- log.push('after-2');
82
- return result;
83
- })
84
- .execute(async () => {
85
- log.push('execute');
86
- return { done: true };
87
- });
88
-
89
- await tool.run({});
90
- expect(log).toEqual(['before-1', 'before-2', 'execute', 'after-2', 'after-1']);
91
- });
92
-
93
- it('should pass context through middleware', async () => {
94
- const tool = aui
95
- .tool('context-test')
96
- .input(z.object({ value: z.number() }))
97
- .middleware(async ({ input, ctx, next }) => {
98
- ctx.cache.set('multiplier', 2);
99
- return next();
100
- })
101
- .execute(async ({ input, ctx }) => {
102
- const multiplier = ctx?.cache.get('multiplier') || 1;
103
- return { result: input.value * multiplier };
104
- });
105
-
106
- const result = await tool.run({ value: 5 });
107
- expect(result).toEqual({ result: 10 });
108
- });
109
- });
110
-
111
- describe('Tool Registry', () => {
112
- it('should register and retrieve tools', () => {
113
- const tool1 = aui.tool('tool1').execute(async () => ({}));
114
- const tool2 = aui.tool('tool2').execute(async () => ({}));
115
-
116
- expect(aui.has('tool1')).toBe(true);
117
- expect(aui.has('tool2')).toBe(true);
118
- expect(aui.getToolNames()).toContain('tool1');
119
- expect(aui.getToolNames()).toContain('tool2');
120
- });
121
-
122
- it('should support tags', () => {
123
- const tool1 = aui.tool('tagged1').tag('ai', 'control').execute(async () => ({}));
124
- const tool2 = aui.tool('tagged2').tag('ai', 'navigation').execute(async () => ({}));
125
- const tool3 = aui.tool('tagged3').tag('control').execute(async () => ({}));
126
-
127
- const aiTools = aui.findByTag('ai');
128
- expect(aiTools).toHaveLength(2);
129
-
130
- const controlTools = aui.findByTags('ai', 'control');
131
- expect(controlTools).toHaveLength(1);
132
- expect(controlTools[0].name).toBe('tagged1');
133
- });
134
- });
135
-
136
- describe('Tool Metadata', () => {
137
- it('should store and retrieve metadata', () => {
138
- const tool = aui
139
- .tool('documented')
140
- .describe('This tool does something important')
141
- .tag('important', 'documented')
142
- .input(z.object({ data: z.string() }))
143
- .execute(async ({ input }) => ({ processed: input.data }));
144
-
145
- expect(tool.name).toBe('documented');
146
- expect(tool.description).toBe('This tool does something important');
147
- expect(tool.tags).toEqual(['important', 'documented']);
148
- });
149
-
150
- it('should serialize to JSON', () => {
151
- const tool = aui
152
- .tool('json-tool')
153
- .describe('JSON serializable tool')
154
- .input(z.object({ x: z.number() }))
155
- .execute(async () => ({}))
156
- .clientExecute(async () => ({}))
157
- .middleware(async ({ next }) => next());
158
-
159
- const json = tool.toJSON();
160
- expect(json).toEqual({
161
- name: 'json-tool',
162
- description: 'JSON serializable tool',
163
- tags: [],
164
- hasInput: true,
165
- hasExecute: true,
166
- hasClientExecute: true,
167
- hasRender: false,
168
- hasMiddleware: true
169
- });
170
- });
171
- });
172
-
173
- describe('Caching Context', () => {
174
- it('should cache results in context', async () => {
175
- let executionCount = 0;
176
-
177
- const tool = aui
178
- .tool('cacheable')
179
- .input(z.object({ key: z.string() }))
180
- .clientExecute(async ({ input, ctx }) => {
181
- const cached = ctx.cache.get(input.key);
182
- if (cached) return cached;
183
-
184
- executionCount++;
185
- const result = { value: `computed-${input.key}`, count: executionCount };
186
- ctx.cache.set(input.key, result);
187
- return result;
188
- });
189
-
190
- const ctx = aui.createContext({ isServer: false });
191
-
192
- const result1 = await tool.run({ key: 'test' }, ctx);
193
- const result2 = await tool.run({ key: 'test' }, ctx);
194
- const result3 = await tool.run({ key: 'other' }, ctx);
195
-
196
- expect(result1).toEqual(result2);
197
- expect(executionCount).toBe(2); // Once for 'test', once for 'other'
198
- });
199
- });
200
- });