librechat-data-provider 0.7.430 → 0.7.692
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/check_updates.sh +1 -0
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-query/index.es.js +1 -1
- package/dist/react-query/index.es.js.map +1 -1
- package/package.json +4 -4
- package/server-rollup.config.js +2 -2
- package/specs/actions.spec.ts +299 -28
- package/specs/azure.spec.ts +8 -5
- package/src/actions.ts +114 -43
- package/src/api-endpoints.ts +16 -11
- package/src/azure.ts +38 -32
- package/src/config.ts +126 -68
- package/src/data-service.ts +61 -6
- package/src/file-config.ts +3 -2
- package/src/generate.ts +1 -1
- package/src/index.ts +6 -4
- package/src/keys.ts +2 -0
- package/src/mcp.ts +71 -0
- package/src/models.ts +1 -1
- package/src/react-query/react-query-service.ts +17 -20
- package/src/request.ts +21 -7
- package/src/schemas.ts +104 -23
- package/src/types/agents.ts +12 -0
- package/src/types/assistants.ts +19 -2
- package/src/types/files.ts +1 -0
- package/src/types/mutations.ts +55 -5
- package/src/types/queries.ts +17 -10
- package/src/types.ts +20 -69
- package/src/zod.spec.ts +467 -0
- package/src/zod.ts +66 -0
- package/src/sse.js +0 -242
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
1
|
+
import type OpenAI from 'openai';
|
|
2
2
|
import type { InfiniteData } from '@tanstack/react-query';
|
|
3
3
|
import type {
|
|
4
4
|
TMessage,
|
|
@@ -10,10 +10,7 @@ import type {
|
|
|
10
10
|
TConversationTag,
|
|
11
11
|
TBanner,
|
|
12
12
|
} from './schemas';
|
|
13
|
-
import type { TSpecsConfig } from './models';
|
|
14
13
|
export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
|
|
15
|
-
export type TOpenAIFunction = OpenAI.Chat.ChatCompletionCreateParams.Function;
|
|
16
|
-
export type TOpenAIFunctionCall = OpenAI.Chat.ChatCompletionCreateParams.FunctionCallOption;
|
|
17
14
|
|
|
18
15
|
export * from './schemas';
|
|
19
16
|
|
|
@@ -64,6 +61,7 @@ export type TSubmission = {
|
|
|
64
61
|
initialResponse?: TMessage;
|
|
65
62
|
conversation: Partial<TConversation>;
|
|
66
63
|
endpointOption: TEndpointOption;
|
|
64
|
+
clientTimestamp?: string;
|
|
67
65
|
};
|
|
68
66
|
|
|
69
67
|
export type EventSubmission = Omit<TSubmission, 'initialResponse'> & { initialResponse: TMessage };
|
|
@@ -72,14 +70,13 @@ export type TPluginAction = {
|
|
|
72
70
|
pluginKey: string;
|
|
73
71
|
action: 'install' | 'uninstall';
|
|
74
72
|
auth?: unknown;
|
|
75
|
-
|
|
73
|
+
isEntityTool?: boolean;
|
|
76
74
|
};
|
|
77
75
|
|
|
78
76
|
export type GroupedConversations = [key: string, TConversation[]][];
|
|
79
77
|
|
|
80
78
|
export type TUpdateUserPlugins = {
|
|
81
|
-
|
|
82
|
-
isAgentTool?: boolean;
|
|
79
|
+
isEntityTool?: boolean;
|
|
83
80
|
pluginKey: string;
|
|
84
81
|
action: string;
|
|
85
82
|
auth?: unknown;
|
|
@@ -110,7 +107,7 @@ export type TUser = {
|
|
|
110
107
|
avatar: string;
|
|
111
108
|
role: string;
|
|
112
109
|
provider: string;
|
|
113
|
-
plugins
|
|
110
|
+
plugins?: string[];
|
|
114
111
|
createdAt: string;
|
|
115
112
|
updatedAt: string;
|
|
116
113
|
};
|
|
@@ -152,6 +149,7 @@ export type TUpdateConversationResponse = TConversation;
|
|
|
152
149
|
export type TDeleteConversationRequest = {
|
|
153
150
|
conversationId?: string;
|
|
154
151
|
thread_id?: string;
|
|
152
|
+
endpoint?: string;
|
|
155
153
|
source?: string;
|
|
156
154
|
};
|
|
157
155
|
|
|
@@ -196,12 +194,24 @@ export type TConversationTagRequest = Partial<
|
|
|
196
194
|
|
|
197
195
|
export type TConversationTagResponse = TConversationTag;
|
|
198
196
|
|
|
199
|
-
// type for tagging conversation
|
|
200
197
|
export type TTagConversationRequest = {
|
|
201
198
|
tags: string[];
|
|
199
|
+
tag: string;
|
|
202
200
|
};
|
|
201
|
+
|
|
203
202
|
export type TTagConversationResponse = string[];
|
|
204
203
|
|
|
204
|
+
export type TDuplicateConvoRequest = {
|
|
205
|
+
conversationId?: string;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export type TDuplicateConvoResponse =
|
|
209
|
+
| {
|
|
210
|
+
conversation: TConversation;
|
|
211
|
+
messages: TMessage[];
|
|
212
|
+
}
|
|
213
|
+
| undefined;
|
|
214
|
+
|
|
205
215
|
export type TForkConvoRequest = {
|
|
206
216
|
messageId: string;
|
|
207
217
|
conversationId: string;
|
|
@@ -301,63 +311,6 @@ export type TVerifyEmail = {
|
|
|
301
311
|
|
|
302
312
|
export type TResendVerificationEmail = Omit<TVerifyEmail, 'token'>;
|
|
303
313
|
|
|
304
|
-
export type TInterfaceConfig = {
|
|
305
|
-
privacyPolicy?: {
|
|
306
|
-
externalUrl?: string;
|
|
307
|
-
openNewTab?: boolean;
|
|
308
|
-
};
|
|
309
|
-
termsOfService?: {
|
|
310
|
-
externalUrl?: string;
|
|
311
|
-
openNewTab?: boolean;
|
|
312
|
-
modalAcceptance?: boolean;
|
|
313
|
-
modalTitle?: string;
|
|
314
|
-
modalContent?: string;
|
|
315
|
-
};
|
|
316
|
-
endpointsMenu: boolean;
|
|
317
|
-
modelSelect: boolean;
|
|
318
|
-
parameters: boolean;
|
|
319
|
-
sidePanel: boolean;
|
|
320
|
-
presets: boolean;
|
|
321
|
-
multiConvo: boolean;
|
|
322
|
-
bookmarks: boolean;
|
|
323
|
-
prompts: boolean;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
export type TStartupConfig = {
|
|
327
|
-
appTitle: string;
|
|
328
|
-
socialLogins?: string[];
|
|
329
|
-
interface?: TInterfaceConfig;
|
|
330
|
-
discordLoginEnabled: boolean;
|
|
331
|
-
facebookLoginEnabled: boolean;
|
|
332
|
-
githubLoginEnabled: boolean;
|
|
333
|
-
googleLoginEnabled: boolean;
|
|
334
|
-
openidLoginEnabled: boolean;
|
|
335
|
-
openidLabel: string;
|
|
336
|
-
openidImageUrl: string;
|
|
337
|
-
/** LDAP Auth Configuration */
|
|
338
|
-
ldap?: {
|
|
339
|
-
/** LDAP enabled */
|
|
340
|
-
enabled: boolean;
|
|
341
|
-
/** Whether LDAP uses username vs. email */
|
|
342
|
-
username?: boolean;
|
|
343
|
-
};
|
|
344
|
-
serverDomain: string;
|
|
345
|
-
emailLoginEnabled: boolean;
|
|
346
|
-
registrationEnabled: boolean;
|
|
347
|
-
socialLoginEnabled: boolean;
|
|
348
|
-
passwordResetEnabled: boolean;
|
|
349
|
-
emailEnabled: boolean;
|
|
350
|
-
checkBalance: boolean;
|
|
351
|
-
showBirthdayIcon: boolean;
|
|
352
|
-
helpAndFaqURL: string;
|
|
353
|
-
customFooter?: string;
|
|
354
|
-
modelSpecs?: TSpecsConfig;
|
|
355
|
-
sharedLinksEnabled: boolean;
|
|
356
|
-
publicSharedLinksEnabled: boolean;
|
|
357
|
-
analyticsGtmId?: string;
|
|
358
|
-
instanceProjectId: string;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
314
|
export type TRefreshTokenResponse = {
|
|
362
315
|
token: string;
|
|
363
316
|
user: TUser;
|
|
@@ -491,9 +444,7 @@ export type TUpdatePromptLabelsResponse = {
|
|
|
491
444
|
message: string;
|
|
492
445
|
};
|
|
493
446
|
|
|
494
|
-
export type TDeletePromptGroupResponse =
|
|
495
|
-
promptGroup: string;
|
|
496
|
-
};
|
|
447
|
+
export type TDeletePromptGroupResponse = TUpdatePromptLabelsResponse;
|
|
497
448
|
|
|
498
449
|
export type TDeletePromptGroupRequest = {
|
|
499
450
|
id: string;
|
package/src/zod.spec.ts
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/* eslint-disable jest/no-conditional-expect */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
// zod.spec.ts
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { convertJsonSchemaToZod } from './zod';
|
|
6
|
+
import type { JsonSchemaType } from './zod';
|
|
7
|
+
|
|
8
|
+
describe('convertJsonSchemaToZod', () => {
|
|
9
|
+
describe('primitive types', () => {
|
|
10
|
+
it('should convert string schema', () => {
|
|
11
|
+
const schema: JsonSchemaType = {
|
|
12
|
+
type: 'string',
|
|
13
|
+
};
|
|
14
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
15
|
+
|
|
16
|
+
expect(zodSchema.parse('test')).toBe('test');
|
|
17
|
+
expect(() => zodSchema.parse(123)).toThrow();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should convert string enum schema', () => {
|
|
21
|
+
const schema: JsonSchemaType = {
|
|
22
|
+
type: 'string',
|
|
23
|
+
enum: ['foo', 'bar', 'baz'],
|
|
24
|
+
};
|
|
25
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
26
|
+
|
|
27
|
+
expect(zodSchema.parse('foo')).toBe('foo');
|
|
28
|
+
expect(() => zodSchema.parse('invalid')).toThrow();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should convert number schema', () => {
|
|
32
|
+
const schema: JsonSchemaType = {
|
|
33
|
+
type: 'number',
|
|
34
|
+
};
|
|
35
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
36
|
+
|
|
37
|
+
expect(zodSchema.parse(123)).toBe(123);
|
|
38
|
+
expect(() => zodSchema.parse('123')).toThrow();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should convert boolean schema', () => {
|
|
42
|
+
const schema: JsonSchemaType = {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
};
|
|
45
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
46
|
+
|
|
47
|
+
expect(zodSchema.parse(true)).toBe(true);
|
|
48
|
+
expect(() => zodSchema.parse('true')).toThrow();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('array types', () => {
|
|
53
|
+
it('should convert array of strings schema', () => {
|
|
54
|
+
const schema: JsonSchemaType = {
|
|
55
|
+
type: 'array',
|
|
56
|
+
items: { type: 'string' },
|
|
57
|
+
};
|
|
58
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
59
|
+
|
|
60
|
+
expect(zodSchema.parse(['a', 'b', 'c'])).toEqual(['a', 'b', 'c']);
|
|
61
|
+
expect(() => zodSchema.parse(['a', 123, 'c'])).toThrow();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should convert array of numbers schema', () => {
|
|
65
|
+
const schema: JsonSchemaType = {
|
|
66
|
+
type: 'array',
|
|
67
|
+
items: { type: 'number' },
|
|
68
|
+
};
|
|
69
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
70
|
+
|
|
71
|
+
expect(zodSchema.parse([1, 2, 3])).toEqual([1, 2, 3]);
|
|
72
|
+
expect(() => zodSchema.parse([1, '2', 3])).toThrow();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('object types', () => {
|
|
77
|
+
it('should convert simple object schema', () => {
|
|
78
|
+
const schema: JsonSchemaType = {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
name: { type: 'string' },
|
|
82
|
+
age: { type: 'number' },
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
86
|
+
|
|
87
|
+
expect(zodSchema.parse({ name: 'John', age: 30 })).toEqual({ name: 'John', age: 30 });
|
|
88
|
+
expect(() => zodSchema.parse({ name: 123, age: 30 })).toThrow();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should handle required fields', () => {
|
|
92
|
+
const schema: JsonSchemaType = {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
name: { type: 'string' },
|
|
96
|
+
age: { type: 'number' },
|
|
97
|
+
},
|
|
98
|
+
required: ['name'],
|
|
99
|
+
};
|
|
100
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
101
|
+
|
|
102
|
+
expect(zodSchema.parse({ name: 'John' })).toEqual({ name: 'John' });
|
|
103
|
+
expect(() => zodSchema.parse({})).toThrow();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle nested objects', () => {
|
|
107
|
+
const schema: JsonSchemaType = {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
user: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
name: { type: 'string' },
|
|
114
|
+
age: { type: 'number' },
|
|
115
|
+
},
|
|
116
|
+
required: ['name'],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
required: ['user'],
|
|
120
|
+
};
|
|
121
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
122
|
+
|
|
123
|
+
expect(zodSchema.parse({ user: { name: 'John', age: 30 } })).toEqual({
|
|
124
|
+
user: { name: 'John', age: 30 },
|
|
125
|
+
});
|
|
126
|
+
expect(() => zodSchema.parse({ user: { age: 30 } })).toThrow();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should handle objects with arrays', () => {
|
|
130
|
+
const schema: JsonSchemaType = {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
names: {
|
|
134
|
+
type: 'array',
|
|
135
|
+
items: { type: 'string' },
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
140
|
+
|
|
141
|
+
expect(zodSchema.parse({ names: ['John', 'Jane'] })).toEqual({ names: ['John', 'Jane'] });
|
|
142
|
+
expect(() => zodSchema.parse({ names: ['John', 123] })).toThrow();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('edge cases', () => {
|
|
147
|
+
it('should handle empty object schema', () => {
|
|
148
|
+
const schema: JsonSchemaType = {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {},
|
|
151
|
+
};
|
|
152
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
153
|
+
|
|
154
|
+
expect(zodSchema.parse({})).toEqual({});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should handle unknown types as unknown', () => {
|
|
158
|
+
const schema = {
|
|
159
|
+
type: 'invalid',
|
|
160
|
+
} as unknown as JsonSchemaType;
|
|
161
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
162
|
+
|
|
163
|
+
expect(zodSchema.parse('anything')).toBe('anything');
|
|
164
|
+
expect(zodSchema.parse(123)).toBe(123);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should handle empty enum arrays as regular strings', () => {
|
|
168
|
+
const schema: JsonSchemaType = {
|
|
169
|
+
type: 'string',
|
|
170
|
+
enum: [],
|
|
171
|
+
};
|
|
172
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
173
|
+
|
|
174
|
+
expect(zodSchema.parse('test')).toBe('test');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('complex schemas', () => {
|
|
179
|
+
it('should handle complex nested schema', () => {
|
|
180
|
+
const schema: JsonSchemaType = {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
id: { type: 'number' },
|
|
184
|
+
user: {
|
|
185
|
+
type: 'object',
|
|
186
|
+
properties: {
|
|
187
|
+
name: { type: 'string' },
|
|
188
|
+
roles: {
|
|
189
|
+
type: 'array',
|
|
190
|
+
items: {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
name: { type: 'string' },
|
|
194
|
+
permissions: {
|
|
195
|
+
type: 'array',
|
|
196
|
+
items: {
|
|
197
|
+
type: 'string',
|
|
198
|
+
enum: ['read', 'write', 'admin'],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
required: ['name', 'permissions'],
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
required: ['name', 'roles'],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
required: ['id', 'user'],
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
213
|
+
|
|
214
|
+
const validData = {
|
|
215
|
+
id: 1,
|
|
216
|
+
user: {
|
|
217
|
+
name: 'John',
|
|
218
|
+
roles: [
|
|
219
|
+
{
|
|
220
|
+
name: 'moderator',
|
|
221
|
+
permissions: ['read', 'write'],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
expect(zodSchema.parse(validData)).toEqual(validData);
|
|
228
|
+
expect(() =>
|
|
229
|
+
zodSchema.parse({
|
|
230
|
+
id: 1,
|
|
231
|
+
user: {
|
|
232
|
+
name: 'John',
|
|
233
|
+
roles: [
|
|
234
|
+
{
|
|
235
|
+
name: 'moderator',
|
|
236
|
+
permissions: ['invalid'],
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
241
|
+
).toThrow();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// zod.spec.ts
|
|
246
|
+
describe('schema descriptions', () => {
|
|
247
|
+
it('should preserve top-level description', () => {
|
|
248
|
+
const schema: JsonSchemaType = {
|
|
249
|
+
type: 'object',
|
|
250
|
+
description: 'A test schema description',
|
|
251
|
+
properties: {
|
|
252
|
+
name: { type: 'string' },
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
256
|
+
expect(zodSchema.description).toBe('A test schema description');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should preserve field descriptions', () => {
|
|
260
|
+
const schema: JsonSchemaType = {
|
|
261
|
+
type: 'object',
|
|
262
|
+
properties: {
|
|
263
|
+
name: {
|
|
264
|
+
type: 'string',
|
|
265
|
+
description: 'The user\'s name',
|
|
266
|
+
},
|
|
267
|
+
age: {
|
|
268
|
+
type: 'number',
|
|
269
|
+
description: 'The user\'s age',
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
274
|
+
|
|
275
|
+
const shape = (zodSchema as z.ZodObject<any>).shape;
|
|
276
|
+
expect(shape.name.description).toBe('The user\'s name');
|
|
277
|
+
expect(shape.age.description).toBe('The user\'s age');
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should preserve descriptions in nested objects', () => {
|
|
281
|
+
const schema: JsonSchemaType = {
|
|
282
|
+
type: 'object',
|
|
283
|
+
description: 'User record',
|
|
284
|
+
properties: {
|
|
285
|
+
user: {
|
|
286
|
+
type: 'object',
|
|
287
|
+
description: 'User details',
|
|
288
|
+
properties: {
|
|
289
|
+
name: {
|
|
290
|
+
type: 'string',
|
|
291
|
+
description: 'The user\'s name',
|
|
292
|
+
},
|
|
293
|
+
settings: {
|
|
294
|
+
type: 'object',
|
|
295
|
+
description: 'User preferences',
|
|
296
|
+
properties: {
|
|
297
|
+
theme: {
|
|
298
|
+
type: 'string',
|
|
299
|
+
description: 'UI theme preference',
|
|
300
|
+
enum: ['light', 'dark'],
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
309
|
+
|
|
310
|
+
// Type assertions for better type safety
|
|
311
|
+
const shape = zodSchema instanceof z.ZodObject ? zodSchema.shape : {};
|
|
312
|
+
expect(zodSchema.description).toBe('User record');
|
|
313
|
+
|
|
314
|
+
if ('user' in shape) {
|
|
315
|
+
expect(shape.user.description).toBe('User details');
|
|
316
|
+
|
|
317
|
+
const userShape = shape.user instanceof z.ZodObject ? shape.user.shape : {};
|
|
318
|
+
if ('name' in userShape && 'settings' in userShape) {
|
|
319
|
+
expect(userShape.name.description).toBe('The user\'s name');
|
|
320
|
+
expect(userShape.settings.description).toBe('User preferences');
|
|
321
|
+
|
|
322
|
+
const settingsShape =
|
|
323
|
+
userShape.settings instanceof z.ZodObject ? userShape.settings.shape : {};
|
|
324
|
+
if ('theme' in settingsShape) {
|
|
325
|
+
expect(settingsShape.theme.description).toBe('UI theme preference');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should preserve descriptions in arrays', () => {
|
|
332
|
+
const schema: JsonSchemaType = {
|
|
333
|
+
type: 'object',
|
|
334
|
+
properties: {
|
|
335
|
+
tags: {
|
|
336
|
+
type: 'array',
|
|
337
|
+
description: 'User tags',
|
|
338
|
+
items: {
|
|
339
|
+
type: 'string',
|
|
340
|
+
description: 'Individual tag',
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
scores: {
|
|
344
|
+
type: 'array',
|
|
345
|
+
description: 'Test scores',
|
|
346
|
+
items: {
|
|
347
|
+
type: 'number',
|
|
348
|
+
description: 'Individual score',
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
354
|
+
|
|
355
|
+
const shape = (zodSchema as z.ZodObject<any>).shape;
|
|
356
|
+
expect(shape.tags.description).toBe('User tags');
|
|
357
|
+
expect(shape.scores.description).toBe('Test scores');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should preserve descriptions in enums', () => {
|
|
361
|
+
const schema: JsonSchemaType = {
|
|
362
|
+
type: 'object',
|
|
363
|
+
properties: {
|
|
364
|
+
role: {
|
|
365
|
+
type: 'string',
|
|
366
|
+
description: 'User role in the system',
|
|
367
|
+
enum: ['admin', 'user', 'guest'],
|
|
368
|
+
},
|
|
369
|
+
status: {
|
|
370
|
+
type: 'string',
|
|
371
|
+
description: 'Account status',
|
|
372
|
+
enum: ['active', 'suspended', 'deleted'],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
377
|
+
|
|
378
|
+
const shape = (zodSchema as z.ZodObject<any>).shape;
|
|
379
|
+
expect(shape.role.description).toBe('User role in the system');
|
|
380
|
+
expect(shape.status.description).toBe('Account status');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should preserve descriptions in a complex schema', () => {
|
|
384
|
+
const schema: JsonSchemaType = {
|
|
385
|
+
type: 'object',
|
|
386
|
+
description: 'User profile configuration',
|
|
387
|
+
properties: {
|
|
388
|
+
basicInfo: {
|
|
389
|
+
type: 'object',
|
|
390
|
+
description: 'Basic user information',
|
|
391
|
+
properties: {
|
|
392
|
+
name: {
|
|
393
|
+
type: 'string',
|
|
394
|
+
description: 'Full name of the user',
|
|
395
|
+
},
|
|
396
|
+
age: {
|
|
397
|
+
type: 'number',
|
|
398
|
+
description: 'User age in years',
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
required: ['name'],
|
|
402
|
+
},
|
|
403
|
+
preferences: {
|
|
404
|
+
type: 'object',
|
|
405
|
+
description: 'User preferences',
|
|
406
|
+
properties: {
|
|
407
|
+
notifications: {
|
|
408
|
+
type: 'array',
|
|
409
|
+
description: 'Notification settings',
|
|
410
|
+
items: {
|
|
411
|
+
type: 'object',
|
|
412
|
+
description: 'Individual notification preference',
|
|
413
|
+
properties: {
|
|
414
|
+
type: {
|
|
415
|
+
type: 'string',
|
|
416
|
+
description: 'Type of notification',
|
|
417
|
+
enum: ['email', 'sms', 'push'],
|
|
418
|
+
},
|
|
419
|
+
enabled: {
|
|
420
|
+
type: 'boolean',
|
|
421
|
+
description: 'Whether this notification is enabled',
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
theme: {
|
|
427
|
+
type: 'string',
|
|
428
|
+
description: 'UI theme preference',
|
|
429
|
+
enum: ['light', 'dark', 'system'],
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const zodSchema = convertJsonSchemaToZod(schema);
|
|
437
|
+
|
|
438
|
+
// Test top-level description
|
|
439
|
+
expect(zodSchema.description).toBe('User profile configuration');
|
|
440
|
+
|
|
441
|
+
const shape = zodSchema instanceof z.ZodObject ? zodSchema.shape : {};
|
|
442
|
+
|
|
443
|
+
// Test basic info descriptions
|
|
444
|
+
if ('basicInfo' in shape) {
|
|
445
|
+
expect(shape.basicInfo.description).toBe('Basic user information');
|
|
446
|
+
const basicInfoShape = shape.basicInfo instanceof z.ZodObject ? shape.basicInfo.shape : {};
|
|
447
|
+
|
|
448
|
+
if ('name' in basicInfoShape && 'age' in basicInfoShape) {
|
|
449
|
+
expect(basicInfoShape.name.description).toBe('Full name of the user');
|
|
450
|
+
expect(basicInfoShape.age.description).toBe('User age in years');
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Test preferences descriptions
|
|
455
|
+
if ('preferences' in shape) {
|
|
456
|
+
expect(shape.preferences.description).toBe('User preferences');
|
|
457
|
+
const preferencesShape =
|
|
458
|
+
shape.preferences instanceof z.ZodObject ? shape.preferences.shape : {};
|
|
459
|
+
|
|
460
|
+
if ('notifications' in preferencesShape && 'theme' in preferencesShape) {
|
|
461
|
+
expect(preferencesShape.notifications.description).toBe('Notification settings');
|
|
462
|
+
expect(preferencesShape.theme.description).toBe('UI theme preference');
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
});
|
package/src/zod.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export type JsonSchemaType = {
|
|
4
|
+
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
5
|
+
enum?: string[];
|
|
6
|
+
items?: JsonSchemaType;
|
|
7
|
+
properties?: Record<string, JsonSchemaType>;
|
|
8
|
+
required?: string[];
|
|
9
|
+
description?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function convertJsonSchemaToZod(schema: JsonSchemaType): z.ZodType {
|
|
13
|
+
let zodSchema: z.ZodType;
|
|
14
|
+
|
|
15
|
+
// Handle primitive types
|
|
16
|
+
if (schema.type === 'string') {
|
|
17
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
18
|
+
const [first, ...rest] = schema.enum;
|
|
19
|
+
zodSchema = z.enum([first, ...rest] as [string, ...string[]]);
|
|
20
|
+
} else {
|
|
21
|
+
zodSchema = z.string();
|
|
22
|
+
}
|
|
23
|
+
} else if (schema.type === 'number') {
|
|
24
|
+
zodSchema = z.number();
|
|
25
|
+
} else if (schema.type === 'boolean') {
|
|
26
|
+
zodSchema = z.boolean();
|
|
27
|
+
} else if (schema.type === 'array' && schema.items !== undefined) {
|
|
28
|
+
const itemSchema = convertJsonSchemaToZod(schema.items);
|
|
29
|
+
zodSchema = z.array(itemSchema);
|
|
30
|
+
} else if (schema.type === 'object') {
|
|
31
|
+
const shape: Record<string, z.ZodType> = {};
|
|
32
|
+
const properties = schema.properties ?? {};
|
|
33
|
+
|
|
34
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
35
|
+
let fieldSchema = convertJsonSchemaToZod(value);
|
|
36
|
+
if (value.description != null && value.description !== '') {
|
|
37
|
+
fieldSchema = fieldSchema.describe(value.description);
|
|
38
|
+
}
|
|
39
|
+
shape[key] = fieldSchema;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let objectSchema = z.object(shape);
|
|
43
|
+
|
|
44
|
+
if (Array.isArray(schema.required) && schema.required.length > 0) {
|
|
45
|
+
const partial = Object.fromEntries(
|
|
46
|
+
Object.entries(shape).map(([key, value]) => [
|
|
47
|
+
key,
|
|
48
|
+
schema.required?.includes(key) === true ? value : value.optional(),
|
|
49
|
+
]),
|
|
50
|
+
);
|
|
51
|
+
objectSchema = z.object(partial);
|
|
52
|
+
} else {
|
|
53
|
+
objectSchema = objectSchema.partial();
|
|
54
|
+
}
|
|
55
|
+
zodSchema = objectSchema;
|
|
56
|
+
} else {
|
|
57
|
+
zodSchema = z.unknown();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Add description if present
|
|
61
|
+
if (schema.description != null && schema.description !== '') {
|
|
62
|
+
zodSchema = zodSchema.describe(schema.description);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return zodSchema;
|
|
66
|
+
}
|