@output.ai/llm 0.2.13 → 0.3.0-dev.pr341-d46aaf1
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/package.json +7 -7
- package/src/ai_model.js +24 -6
- package/src/ai_model.spec.js +65 -1
- package/src/ai_sdk.js +19 -43
- package/src/ai_sdk.spec.js +168 -114
- package/src/index.d.ts +79 -30
- package/src/index.js +2 -1
- package/src/prompt_loader_validation.spec.js +9 -88
- package/src/prompt_validations.js +25 -42
- package/src/prompt_validations.spec.js +124 -8
- package/src/validations.js +3 -13
package/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { generateText, generateArray, generateObject, generateEnum } from './ai_sdk.js';
|
|
2
2
|
export { loadPrompt } from './prompt_loader.js';
|
|
3
|
-
export {
|
|
3
|
+
export { registerProvider, getRegisteredProviders } from './ai_model.js';
|
|
4
|
+
export { tool, Output } from 'ai';
|
|
4
5
|
export * as ai from 'ai';
|
|
@@ -6,7 +6,6 @@ vi.mock( './load_content.js', () => ( {
|
|
|
6
6
|
} ) );
|
|
7
7
|
|
|
8
8
|
import { loadContent } from './load_content.js';
|
|
9
|
-
import { ValidationError } from '@output.ai/core';
|
|
10
9
|
|
|
11
10
|
describe( 'loadPrompt - validation with real schema', () => {
|
|
12
11
|
beforeEach( () => {
|
|
@@ -14,7 +13,6 @@ describe( 'loadPrompt - validation with real schema', () => {
|
|
|
14
13
|
} );
|
|
15
14
|
|
|
16
15
|
it( 'should accept valid camelCase format with providerOptions and budgetTokens', () => {
|
|
17
|
-
// Frontmatter uses canonical format: providerOptions.thinking.budgetTokens
|
|
18
16
|
const promptContent = `---
|
|
19
17
|
provider: anthropic
|
|
20
18
|
model: claude-sonnet-4-20250514
|
|
@@ -32,14 +30,12 @@ providerOptions:
|
|
|
32
30
|
|
|
33
31
|
const result = loadPrompt( 'test', {} );
|
|
34
32
|
|
|
35
|
-
// Validation passes with canonical schema format
|
|
36
33
|
expect( result.config.providerOptions ).toBeDefined();
|
|
37
34
|
expect( result.config.providerOptions.thinking.type ).toBe( 'enabled' );
|
|
38
35
|
expect( result.config.providerOptions.thinking.budgetTokens ).toBe( 1500 );
|
|
39
36
|
} );
|
|
40
37
|
|
|
41
|
-
it( 'should
|
|
42
|
-
// Using snake_case max_tokens should fail validation
|
|
38
|
+
it( 'should accept snake_case max_tokens via config passthrough (no longer strict)', () => {
|
|
43
39
|
const promptContent = `---
|
|
44
40
|
provider: anthropic
|
|
45
41
|
model: claude-sonnet-4-20250514
|
|
@@ -50,19 +46,13 @@ max_tokens: 64000
|
|
|
50
46
|
|
|
51
47
|
loadContent.mockReturnValue( promptContent );
|
|
52
48
|
|
|
53
|
-
//
|
|
54
|
-
// Other tests check the actual message, but this one specifically checks that we throw
|
|
55
|
-
// the right kind of error, just to check that as well
|
|
49
|
+
// Config uses passthrough, so max_tokens is accepted (though ignored by SDK)
|
|
56
50
|
expect( () => {
|
|
57
51
|
loadPrompt( 'test', {} );
|
|
58
|
-
} ).toThrow(
|
|
59
|
-
expect( () => {
|
|
60
|
-
loadPrompt( 'test', {} );
|
|
61
|
-
} ).toThrow( /max_tokens/ );
|
|
52
|
+
} ).not.toThrow();
|
|
62
53
|
} );
|
|
63
54
|
|
|
64
|
-
it( 'should
|
|
65
|
-
// Using 'options' instead of 'providerOptions' should fail validation
|
|
55
|
+
it( 'should accept "options" field via config passthrough (no longer strict)', () => {
|
|
66
56
|
const promptContent = `---
|
|
67
57
|
provider: anthropic
|
|
68
58
|
model: claude-sonnet-4-20250514
|
|
@@ -76,85 +66,16 @@ options:
|
|
|
76
66
|
|
|
77
67
|
loadContent.mockReturnValue( promptContent );
|
|
78
68
|
|
|
79
|
-
//
|
|
80
|
-
expect( () => {
|
|
81
|
-
loadPrompt( 'test', {} );
|
|
82
|
-
} ).toThrow( /Invalid prompt file/ );
|
|
83
|
-
} );
|
|
84
|
-
|
|
85
|
-
it( 'should REJECT snake_case budget_tokens in providerOptions', () => {
|
|
86
|
-
// Using snake_case budget_tokens should fail validation
|
|
87
|
-
const promptContent = `---
|
|
88
|
-
provider: anthropic
|
|
89
|
-
model: claude-sonnet-4-20250514
|
|
90
|
-
providerOptions:
|
|
91
|
-
thinking:
|
|
92
|
-
type: enabled
|
|
93
|
-
budget_tokens: 1500
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
<user>Hello</user>`;
|
|
97
|
-
|
|
98
|
-
loadContent.mockReturnValue( promptContent );
|
|
99
|
-
|
|
100
|
-
// loadPrompt should throw ValidationError because schema rejects budget_tokens
|
|
101
|
-
expect( () => {
|
|
102
|
-
loadPrompt( 'test', {} );
|
|
103
|
-
} ).toThrow( /Invalid prompt file/ );
|
|
104
|
-
} );
|
|
105
|
-
|
|
106
|
-
it( 'end-to-end: should accept canonical camelCase format', () => {
|
|
107
|
-
// End-to-end test: frontmatter uses canonical providerOptions.thinking.budgetTokens
|
|
108
|
-
const promptContent = `---
|
|
109
|
-
provider: anthropic
|
|
110
|
-
model: claude-sonnet-4-20250514
|
|
111
|
-
temperature: 0.7
|
|
112
|
-
maxTokens: 64000
|
|
113
|
-
providerOptions:
|
|
114
|
-
thinking:
|
|
115
|
-
type: enabled
|
|
116
|
-
budgetTokens: 1500
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
<user>Hello</user>`;
|
|
120
|
-
|
|
121
|
-
loadContent.mockReturnValue( promptContent );
|
|
122
|
-
|
|
123
|
-
const result = loadPrompt( 'test', {} );
|
|
124
|
-
|
|
125
|
-
// After loadPrompt, uses canonical format
|
|
126
|
-
expect( result.config.providerOptions ).toBeDefined();
|
|
127
|
-
expect( result.config.providerOptions.thinking.type ).toBe( 'enabled' );
|
|
128
|
-
expect( result.config.providerOptions.thinking.budgetTokens ).toBe( 1500 );
|
|
129
|
-
} );
|
|
130
|
-
|
|
131
|
-
it( 'end-to-end: should FAIL validation with snake_case budget_tokens', () => {
|
|
132
|
-
// End-to-end test: using snake_case should fail validation
|
|
133
|
-
const promptContent = `---
|
|
134
|
-
provider: anthropic
|
|
135
|
-
model: claude-sonnet-4-20250514
|
|
136
|
-
providerOptions:
|
|
137
|
-
thinking:
|
|
138
|
-
type: enabled
|
|
139
|
-
budget_tokens: 1500
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
<user>Hello</user>`;
|
|
143
|
-
|
|
144
|
-
loadContent.mockReturnValue( promptContent );
|
|
145
|
-
|
|
146
|
-
// loadPrompt should throw ValidationError because schema rejects budget_tokens
|
|
69
|
+
// Config uses passthrough, so 'options' passes through (though not used)
|
|
147
70
|
expect( () => {
|
|
148
71
|
loadPrompt( 'test', {} );
|
|
149
|
-
} ).toThrow(
|
|
72
|
+
} ).not.toThrow();
|
|
150
73
|
} );
|
|
151
74
|
|
|
152
|
-
it( '
|
|
153
|
-
// End-to-end test: using snake_case should fail validation
|
|
75
|
+
it( 'should accept snake_case budget_tokens in thinking via passthrough', () => {
|
|
154
76
|
const promptContent = `---
|
|
155
77
|
provider: anthropic
|
|
156
78
|
model: claude-sonnet-4-20250514
|
|
157
|
-
max_tokens: 4000
|
|
158
79
|
providerOptions:
|
|
159
80
|
thinking:
|
|
160
81
|
type: enabled
|
|
@@ -165,10 +86,10 @@ providerOptions:
|
|
|
165
86
|
|
|
166
87
|
loadContent.mockReturnValue( promptContent );
|
|
167
88
|
|
|
168
|
-
//
|
|
89
|
+
// budget_tokens is silently stripped from thinking (unknown field), not rejected
|
|
169
90
|
expect( () => {
|
|
170
91
|
loadPrompt( 'test', {} );
|
|
171
|
-
} ).toThrow(
|
|
92
|
+
} ).not.toThrow();
|
|
172
93
|
} );
|
|
173
94
|
|
|
174
95
|
} );
|
|
@@ -3,23 +3,18 @@ import { ValidationError, z } from '@output.ai/core';
|
|
|
3
3
|
export const promptSchema = z.object( {
|
|
4
4
|
name: z.string(),
|
|
5
5
|
config: z.object( {
|
|
6
|
-
provider: z.
|
|
6
|
+
provider: z.string().min( 1 ),
|
|
7
7
|
model: z.string(),
|
|
8
8
|
temperature: z.number().optional(),
|
|
9
9
|
maxTokens: z.number().optional(),
|
|
10
10
|
tools: z.record( z.string(), z.object( {} ).passthrough() ).optional(),
|
|
11
11
|
providerOptions: z.object( {
|
|
12
12
|
thinking: z.object( {
|
|
13
|
-
type: z.
|
|
14
|
-
budgetTokens: z.number()
|
|
15
|
-
} ).optional()
|
|
16
|
-
anthropic: z.record( z.string(), z.unknown() ).optional(),
|
|
17
|
-
openai: z.record( z.string(), z.unknown() ).optional(),
|
|
18
|
-
azure: z.record( z.string(), z.unknown() ).optional(),
|
|
19
|
-
vertex: z.record( z.string(), z.unknown() ).optional(),
|
|
20
|
-
google: z.record( z.string(), z.unknown() ).optional()
|
|
13
|
+
type: z.enum( [ 'enabled', 'disabled' ] ),
|
|
14
|
+
budgetTokens: z.number().optional()
|
|
15
|
+
} ).passthrough().optional()
|
|
21
16
|
} ).passthrough().optional()
|
|
22
|
-
} ).
|
|
17
|
+
} ).passthrough(),
|
|
23
18
|
messages: z.array(
|
|
24
19
|
z.object( {
|
|
25
20
|
role: z.string(),
|
|
@@ -28,50 +23,38 @@ export const promptSchema = z.object( {
|
|
|
28
23
|
)
|
|
29
24
|
} ).strict();
|
|
30
25
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if ( errorMessage.includes( '"options"' ) ) {
|
|
39
|
-
return '\nHint: Use "providerOptions" instead of "options".';
|
|
40
|
-
}
|
|
41
|
-
return '';
|
|
26
|
+
const SNAKE_CASE_WARNINGS = {
|
|
27
|
+
max_tokens: 'maxTokens',
|
|
28
|
+
budget_tokens: 'budgetTokens',
|
|
29
|
+
top_p: 'topP',
|
|
30
|
+
top_k: 'topK',
|
|
31
|
+
stop_sequences: 'stopSequences',
|
|
32
|
+
options: 'providerOptions'
|
|
42
33
|
};
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
] );
|
|
35
|
+
function warnSnakeCaseFields( config ) {
|
|
36
|
+
for ( const [ snake, camel ] of Object.entries( SNAKE_CASE_WARNINGS ) ) {
|
|
37
|
+
if ( snake in config ) {
|
|
38
|
+
console.warn( `[output-llm] "${snake}" found in prompt config. Did you mean "${camel}"?` );
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const thinking = config.providerOptions?.thinking;
|
|
42
|
+
if ( thinking && 'budget_tokens' in thinking ) {
|
|
43
|
+
console.warn( '[output-llm] "budget_tokens" found in providerOptions.thinking. Did you mean "budgetTokens"?' );
|
|
44
|
+
}
|
|
45
|
+
}
|
|
56
46
|
|
|
57
47
|
export function validatePrompt( prompt ) {
|
|
58
48
|
const result = promptSchema.safeParse( prompt );
|
|
59
49
|
if ( !result.success ) {
|
|
60
50
|
const promptIdentifier = prompt?.name ? `"${prompt.name}"` : '(unnamed)';
|
|
61
51
|
const errorMessage = z.prettifyError( result.error );
|
|
62
|
-
const hint = getHintForError( errorMessage );
|
|
63
52
|
|
|
64
53
|
throw new ValidationError(
|
|
65
|
-
`Invalid prompt file ${promptIdentifier}: ${errorMessage}
|
|
54
|
+
`Invalid prompt file ${promptIdentifier}: ${errorMessage}`,
|
|
66
55
|
{ cause: result.error }
|
|
67
56
|
);
|
|
68
57
|
}
|
|
69
58
|
|
|
70
|
-
|
|
71
|
-
if ( providerOptions ) {
|
|
72
|
-
const unknownFields = Object.keys( providerOptions ).filter( k => !knownProviderOptionsFields.has( k ) );
|
|
73
|
-
if ( unknownFields.length > 0 ) {
|
|
74
|
-
console.warn( `Prompt "${prompt.name}": Unrecognized providerOptions fields: ${unknownFields.join( ', ' )}` );
|
|
75
|
-
}
|
|
76
|
-
}
|
|
59
|
+
warnSnakeCaseFields( result.data.config );
|
|
77
60
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { ValidationError } from '@output.ai/core';
|
|
3
3
|
import { validatePrompt } from './prompt_validations.js';
|
|
4
4
|
|
|
@@ -65,6 +65,52 @@ describe( 'validatePrompt', () => {
|
|
|
65
65
|
expect( () => validatePrompt( promptWithThinking ) ).not.toThrow();
|
|
66
66
|
} );
|
|
67
67
|
|
|
68
|
+
it( 'should validate a prompt with thinking type disabled', () => {
|
|
69
|
+
const promptWithThinkingDisabled = {
|
|
70
|
+
name: 'thinking-disabled-prompt',
|
|
71
|
+
config: {
|
|
72
|
+
provider: 'anthropic',
|
|
73
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
74
|
+
providerOptions: {
|
|
75
|
+
thinking: {
|
|
76
|
+
type: 'disabled'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
messages: [
|
|
81
|
+
{
|
|
82
|
+
role: 'user',
|
|
83
|
+
content: 'Simple task.'
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
expect( () => validatePrompt( promptWithThinkingDisabled ) ).not.toThrow();
|
|
89
|
+
} );
|
|
90
|
+
|
|
91
|
+
it( 'should validate a prompt with thinking without budgetTokens', () => {
|
|
92
|
+
const promptWithThinkingNoBudget = {
|
|
93
|
+
name: 'thinking-no-budget',
|
|
94
|
+
config: {
|
|
95
|
+
provider: 'anthropic',
|
|
96
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
97
|
+
providerOptions: {
|
|
98
|
+
thinking: {
|
|
99
|
+
type: 'enabled'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
messages: [
|
|
104
|
+
{
|
|
105
|
+
role: 'user',
|
|
106
|
+
content: 'Think about this.'
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
expect( () => validatePrompt( promptWithThinkingNoBudget ) ).not.toThrow();
|
|
112
|
+
} );
|
|
113
|
+
|
|
68
114
|
it( 'should validate a prompt with anthropic-specific providerOptions', () => {
|
|
69
115
|
const promptWithAnthropicOptions = {
|
|
70
116
|
name: 'anthropic-options-prompt',
|
|
@@ -174,11 +220,50 @@ describe( 'validatePrompt', () => {
|
|
|
174
220
|
expect( () => validatePrompt( promptWithMixedOptions ) ).not.toThrow();
|
|
175
221
|
} );
|
|
176
222
|
|
|
177
|
-
it( 'should
|
|
178
|
-
const
|
|
179
|
-
name: '
|
|
223
|
+
it( 'should accept custom provider names for dynamic providers', () => {
|
|
224
|
+
const customProviderPrompt = {
|
|
225
|
+
name: 'custom-provider-prompt',
|
|
226
|
+
config: {
|
|
227
|
+
provider: 'my-custom-provider',
|
|
228
|
+
model: 'custom-model-v1'
|
|
229
|
+
},
|
|
230
|
+
messages: [
|
|
231
|
+
{
|
|
232
|
+
role: 'user',
|
|
233
|
+
content: 'Test'
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
expect( () => validatePrompt( customProviderPrompt ) ).not.toThrow();
|
|
239
|
+
} );
|
|
240
|
+
|
|
241
|
+
it( 'should accept extra config fields via passthrough', () => {
|
|
242
|
+
const extraFieldsPrompt = {
|
|
243
|
+
name: 'extra-fields-prompt',
|
|
244
|
+
config: {
|
|
245
|
+
provider: 'openai',
|
|
246
|
+
model: 'gpt-4',
|
|
247
|
+
topP: 0.9,
|
|
248
|
+
seed: 42,
|
|
249
|
+
stopSequences: [ 'END' ]
|
|
250
|
+
},
|
|
251
|
+
messages: [
|
|
252
|
+
{
|
|
253
|
+
role: 'user',
|
|
254
|
+
content: 'Test'
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
expect( () => validatePrompt( extraFieldsPrompt ) ).not.toThrow();
|
|
260
|
+
} );
|
|
261
|
+
|
|
262
|
+
it( 'should throw ValidationError when provider is empty string', () => {
|
|
263
|
+
const emptyProviderPrompt = {
|
|
264
|
+
name: 'empty-provider',
|
|
180
265
|
config: {
|
|
181
|
-
provider: '
|
|
266
|
+
provider: '',
|
|
182
267
|
model: 'some-model'
|
|
183
268
|
},
|
|
184
269
|
messages: [
|
|
@@ -189,7 +274,7 @@ describe( 'validatePrompt', () => {
|
|
|
189
274
|
]
|
|
190
275
|
};
|
|
191
276
|
|
|
192
|
-
expect( () => validatePrompt(
|
|
277
|
+
expect( () => validatePrompt( emptyProviderPrompt ) ).toThrow( ValidationError );
|
|
193
278
|
} );
|
|
194
279
|
|
|
195
280
|
it( 'should throw ValidationError when required fields are missing', () => {
|
|
@@ -209,7 +294,38 @@ describe( 'validatePrompt', () => {
|
|
|
209
294
|
expect( () => validatePrompt( missingNamePrompt ) ).toThrow( ValidationError );
|
|
210
295
|
} );
|
|
211
296
|
|
|
212
|
-
it( 'should
|
|
297
|
+
it( 'should pass through budget_tokens in thinking and warn about snake_case', () => {
|
|
298
|
+
const warnSpy = vi.spyOn( console, 'warn' ).mockImplementation( () => {} );
|
|
299
|
+
|
|
300
|
+
const promptWithBudgetTokensSnake = {
|
|
301
|
+
name: 'thinking-budget-snake',
|
|
302
|
+
config: {
|
|
303
|
+
provider: 'anthropic',
|
|
304
|
+
model: 'claude-sonnet-4-20250514',
|
|
305
|
+
providerOptions: {
|
|
306
|
+
thinking: {
|
|
307
|
+
type: 'enabled',
|
|
308
|
+
budget_tokens: 10000
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
messages: [
|
|
313
|
+
{
|
|
314
|
+
role: 'user',
|
|
315
|
+
content: 'Think hard.'
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
expect( () => validatePrompt( promptWithBudgetTokensSnake ) ).not.toThrow();
|
|
321
|
+
expect( warnSpy ).toHaveBeenCalledWith(
|
|
322
|
+
'[output-llm] "budget_tokens" found in providerOptions.thinking. Did you mean "budgetTokens"?'
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
warnSpy.mockRestore();
|
|
326
|
+
} );
|
|
327
|
+
|
|
328
|
+
it( 'should allow snake_case fields in config via passthrough (no longer strict)', () => {
|
|
213
329
|
const maxTokensSnakeCase = {
|
|
214
330
|
name: 'test-prompt',
|
|
215
331
|
config: {
|
|
@@ -225,6 +341,6 @@ describe( 'validatePrompt', () => {
|
|
|
225
341
|
]
|
|
226
342
|
};
|
|
227
343
|
|
|
228
|
-
expect( () => validatePrompt( maxTokensSnakeCase ) ).toThrow(
|
|
344
|
+
expect( () => validatePrompt( maxTokensSnakeCase ) ).not.toThrow();
|
|
229
345
|
} );
|
|
230
346
|
} );
|
package/src/validations.js
CHANGED
|
@@ -5,17 +5,7 @@ const generateTextArgsSchema = z.object( {
|
|
|
5
5
|
variables: z.any().optional()
|
|
6
6
|
} );
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
prompt: z.string(),
|
|
10
|
-
variables: z.any().optional(),
|
|
11
|
-
schema: z.custom( v => v instanceof z.ZodObject, {
|
|
12
|
-
message: 'schema must be a ZodObject'
|
|
13
|
-
} ),
|
|
14
|
-
schemaName: z.string().optional(),
|
|
15
|
-
schemaDescription: z.string().optional()
|
|
16
|
-
} );
|
|
17
|
-
|
|
18
|
-
const generateArrayArgsSchema = z.object( {
|
|
8
|
+
const generateSchemaArgsSchema = z.object( {
|
|
19
9
|
prompt: z.string(),
|
|
20
10
|
variables: z.any().optional(),
|
|
21
11
|
schema: z.custom( v => v instanceof z.ZodType, {
|
|
@@ -43,11 +33,11 @@ export function validateGenerateTextArgs( args ) {
|
|
|
43
33
|
}
|
|
44
34
|
|
|
45
35
|
export function validateGenerateObjectArgs( args ) {
|
|
46
|
-
validateSchema(
|
|
36
|
+
validateSchema( generateSchemaArgsSchema, args, 'Invalid generateObject() arguments' );
|
|
47
37
|
}
|
|
48
38
|
|
|
49
39
|
export function validateGenerateArrayArgs( args ) {
|
|
50
|
-
validateSchema(
|
|
40
|
+
validateSchema( generateSchemaArgsSchema, args, 'Invalid generateArray() arguments' );
|
|
51
41
|
}
|
|
52
42
|
|
|
53
43
|
export function validateGenerateEnumArgs( args ) {
|