agentnet 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/LICENSE.txt +202 -0
- package/README.md +488 -0
- package/package.json +23 -0
- package/src/agent/agent-loader.js +274 -0
- package/src/agent/agent.js +416 -0
- package/src/agent/client.js +14 -0
- package/src/agent/executor.js +320 -0
- package/src/agent/runtime.js +142 -0
- package/src/agent/runtimes/nats.js +379 -0
- package/src/errors/index.js +195 -0
- package/src/examples/agents-smartness.yaml +308 -0
- package/src/examples/agents.yaml +394 -0
- package/src/examples/def.js +74 -0
- package/src/examples/def2.js +65 -0
- package/src/examples/def3.js +67 -0
- package/src/examples/simple.js +103 -0
- package/src/index.js +115 -0
- package/src/llm/gemini.js +155 -0
- package/src/llm/gpt.js +155 -0
- package/src/store/store.js +167 -0
- package/src/utils/logger.js +209 -0
- package/src/utils/store.js +212 -0
- package/src/utils/validation.js +287 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { logger } from './logger.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base store interface
|
|
5
|
+
*/
|
|
6
|
+
class Store {
|
|
7
|
+
/**
|
|
8
|
+
* Gets a value by key
|
|
9
|
+
* @param {string} key - The key to get
|
|
10
|
+
* @returns {Promise<string>} The value
|
|
11
|
+
*/
|
|
12
|
+
async get(key) {
|
|
13
|
+
throw new Error('Method not implemented');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sets a key to a value
|
|
18
|
+
* @param {string} key - The key to set
|
|
19
|
+
* @param {string} value - The value to set
|
|
20
|
+
* @returns {Promise<void>}
|
|
21
|
+
*/
|
|
22
|
+
async set(key, value) {
|
|
23
|
+
throw new Error('Method not implemented');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Deletes a key
|
|
28
|
+
* @param {string} key - The key to delete
|
|
29
|
+
* @returns {Promise<boolean>} Whether the key was deleted
|
|
30
|
+
*/
|
|
31
|
+
async delete(key) {
|
|
32
|
+
throw new Error('Method not implemented');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* In-memory store implementation
|
|
38
|
+
*/
|
|
39
|
+
export class MemoryStore extends Store {
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
this.store = new Map();
|
|
43
|
+
logger.debug('Initialized in-memory store');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets a value by key
|
|
48
|
+
* @param {string} key - The key to get
|
|
49
|
+
* @returns {Promise<string>} The value
|
|
50
|
+
*/
|
|
51
|
+
async get(key) {
|
|
52
|
+
logger.debug(`MemoryStore: Getting key ${key}`);
|
|
53
|
+
return this.store.get(key);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Sets a key to a value
|
|
58
|
+
* @param {string} key - The key to set
|
|
59
|
+
* @param {string} value - The value to set
|
|
60
|
+
* @returns {Promise<void>}
|
|
61
|
+
*/
|
|
62
|
+
async set(key, value) {
|
|
63
|
+
logger.debug(`MemoryStore: Setting key ${key}`);
|
|
64
|
+
this.store.set(key, value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Deletes a key
|
|
69
|
+
* @param {string} key - The key to delete
|
|
70
|
+
* @returns {Promise<boolean>} Whether the key was deleted
|
|
71
|
+
*/
|
|
72
|
+
async delete(key) {
|
|
73
|
+
logger.debug(`MemoryStore: Deleting key ${key}`);
|
|
74
|
+
return this.store.delete(key);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Gets all keys
|
|
79
|
+
* @returns {Promise<Array<string>>} All keys in the store
|
|
80
|
+
*/
|
|
81
|
+
async keys() {
|
|
82
|
+
return Array.from(this.store.keys());
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Clears the store
|
|
87
|
+
* @returns {Promise<void>}
|
|
88
|
+
*/
|
|
89
|
+
async clear() {
|
|
90
|
+
logger.debug('MemoryStore: Clearing all keys');
|
|
91
|
+
this.store.clear();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Redis store implementation
|
|
97
|
+
*/
|
|
98
|
+
export class RedisStore extends Store {
|
|
99
|
+
/**
|
|
100
|
+
* Creates a new Redis store
|
|
101
|
+
* @param {Object} client - Redis client
|
|
102
|
+
* @param {Object} options - Store options
|
|
103
|
+
* @param {string} options.prefix - Key prefix
|
|
104
|
+
* @param {number} options.ttl - Time to live in seconds
|
|
105
|
+
*/
|
|
106
|
+
constructor(client, options = {}) {
|
|
107
|
+
super();
|
|
108
|
+
this.client = client;
|
|
109
|
+
this.prefix = options.prefix || 'smartagent:session:';
|
|
110
|
+
this.ttl = options.ttl || 86400; // 24 hours default
|
|
111
|
+
logger.debug('Initialized Redis store', { prefix: this.prefix, ttl: this.ttl });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Gets the full key with prefix
|
|
116
|
+
* @param {string} key - The base key
|
|
117
|
+
* @returns {string} The prefixed key
|
|
118
|
+
*/
|
|
119
|
+
_getKey(key) {
|
|
120
|
+
return `${this.prefix}${key}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gets a value by key
|
|
125
|
+
* @param {string} key - The key to get
|
|
126
|
+
* @returns {Promise<string>} The value
|
|
127
|
+
*/
|
|
128
|
+
async get(key) {
|
|
129
|
+
const fullKey = this._getKey(key);
|
|
130
|
+
logger.debug(`RedisStore: Getting key ${fullKey}`);
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
return await this.client.get(fullKey);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
logger.error(`RedisStore: Failed to get key ${fullKey}`, { error });
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Sets a key to a value
|
|
142
|
+
* @param {string} key - The key to set
|
|
143
|
+
* @param {string} value - The value to set
|
|
144
|
+
* @returns {Promise<void>}
|
|
145
|
+
*/
|
|
146
|
+
async set(key, value) {
|
|
147
|
+
const fullKey = this._getKey(key);
|
|
148
|
+
logger.debug(`RedisStore: Setting key ${fullKey}`);
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
await this.client.set(fullKey, value, 'EX', this.ttl);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
logger.error(`RedisStore: Failed to set key ${fullKey}`, { error });
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Deletes a key
|
|
160
|
+
* @param {string} key - The key to delete
|
|
161
|
+
* @returns {Promise<boolean>} Whether the key was deleted
|
|
162
|
+
*/
|
|
163
|
+
async delete(key) {
|
|
164
|
+
const fullKey = this._getKey(key);
|
|
165
|
+
logger.debug(`RedisStore: Deleting key ${fullKey}`);
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const result = await this.client.del(fullKey);
|
|
169
|
+
return result > 0;
|
|
170
|
+
} catch (error) {
|
|
171
|
+
logger.error(`RedisStore: Failed to delete key ${fullKey}`, { error });
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Gets all keys matching the prefix
|
|
178
|
+
* @returns {Promise<Array<string>>} All keys in the store
|
|
179
|
+
*/
|
|
180
|
+
async keys() {
|
|
181
|
+
try {
|
|
182
|
+
const keys = await this.client.keys(`${this.prefix}*`);
|
|
183
|
+
return keys.map(key => key.substring(this.prefix.length));
|
|
184
|
+
} catch (error) {
|
|
185
|
+
logger.error(`RedisStore: Failed to get keys`, { error });
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Creates an appropriate store based on configuration
|
|
193
|
+
* @param {Object} config - Store configuration
|
|
194
|
+
* @returns {Store} The configured store
|
|
195
|
+
*/
|
|
196
|
+
export function createStore(config = {}) {
|
|
197
|
+
const { type = 'memory', ...options } = config;
|
|
198
|
+
|
|
199
|
+
switch (type.toLowerCase()) {
|
|
200
|
+
case 'memory':
|
|
201
|
+
return new MemoryStore();
|
|
202
|
+
|
|
203
|
+
case 'redis':
|
|
204
|
+
if (!options.client) {
|
|
205
|
+
throw new Error('Redis client required for Redis store');
|
|
206
|
+
}
|
|
207
|
+
return new RedisStore(options.client, options);
|
|
208
|
+
|
|
209
|
+
default:
|
|
210
|
+
throw new Error(`Unknown store type: ${type}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { ValidationError } from '../errors/index.js';
|
|
2
|
+
import { logger } from './logger.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validates that a value is not null or undefined
|
|
6
|
+
* @param {any} value - Value to check
|
|
7
|
+
* @param {string} name - Name of the value for error messages
|
|
8
|
+
* @param {string} context - Context for logging
|
|
9
|
+
* @throws {ValidationError} If validation fails
|
|
10
|
+
*/
|
|
11
|
+
export function validateRequired(value, name, context = '') {
|
|
12
|
+
if (value === undefined || value === null) {
|
|
13
|
+
const errorMsg = `${name} is required`;
|
|
14
|
+
logger.error(errorMsg, { context });
|
|
15
|
+
throw new ValidationError(errorMsg, [{ field: name, message: 'Required field is missing' }]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validates that a value is of a specific type
|
|
21
|
+
* @param {any} value - Value to check
|
|
22
|
+
* @param {string} expectedType - Expected type ('string', 'number', 'boolean', 'object', 'function', 'array')
|
|
23
|
+
* @param {string} name - Name of the value for error messages
|
|
24
|
+
* @param {string} context - Context for logging
|
|
25
|
+
* @throws {ValidationError} If validation fails
|
|
26
|
+
*/
|
|
27
|
+
export function validateType(value, expectedType, name, context = '') {
|
|
28
|
+
if (value === undefined || value === null) {
|
|
29
|
+
return; // Skip type validation for null/undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let valid = false;
|
|
33
|
+
|
|
34
|
+
if (expectedType === 'array') {
|
|
35
|
+
valid = Array.isArray(value);
|
|
36
|
+
} else if (expectedType === 'object' && Array.isArray(value)) {
|
|
37
|
+
valid = false; // Arrays are not objects for this validation
|
|
38
|
+
} else {
|
|
39
|
+
valid = typeof value === expectedType;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!valid) {
|
|
43
|
+
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
44
|
+
const errorMsg = `${name} must be of type ${expectedType}, got ${actualType}`;
|
|
45
|
+
logger.error(errorMsg, { context, value: String(value).substring(0, 100) });
|
|
46
|
+
throw new ValidationError(errorMsg, [{
|
|
47
|
+
field: name,
|
|
48
|
+
message: `Expected ${expectedType}, got ${actualType}`,
|
|
49
|
+
actual: actualType,
|
|
50
|
+
expected: expectedType
|
|
51
|
+
}]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Validates that a string matches a regex pattern
|
|
57
|
+
* @param {string} value - String to validate
|
|
58
|
+
* @param {RegExp} pattern - Regex pattern to match
|
|
59
|
+
* @param {string} name - Name of the value for error messages
|
|
60
|
+
* @param {string} context - Context for logging
|
|
61
|
+
* @throws {ValidationError} If validation fails
|
|
62
|
+
*/
|
|
63
|
+
export function validatePattern(value, pattern, name, context = '') {
|
|
64
|
+
if (value === undefined || value === null) {
|
|
65
|
+
return; // Skip pattern validation for null/undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeof value !== 'string') {
|
|
69
|
+
const errorMsg = `${name} must be a string for pattern validation`;
|
|
70
|
+
logger.error(errorMsg, { context });
|
|
71
|
+
throw new ValidationError(errorMsg, [{
|
|
72
|
+
field: name,
|
|
73
|
+
message: 'Must be a string for pattern validation'
|
|
74
|
+
}]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!pattern.test(value)) {
|
|
78
|
+
const errorMsg = `${name} does not match required pattern`;
|
|
79
|
+
logger.error(errorMsg, { context, pattern: pattern.toString() });
|
|
80
|
+
throw new ValidationError(errorMsg, [{
|
|
81
|
+
field: name,
|
|
82
|
+
message: 'Value does not match required pattern',
|
|
83
|
+
pattern: pattern.toString()
|
|
84
|
+
}]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validates an object against a schema
|
|
90
|
+
* @param {Object} obj - Object to validate
|
|
91
|
+
* @param {Object} schema - Validation schema with field definitions
|
|
92
|
+
* @param {string} context - Context for logging
|
|
93
|
+
* @throws {ValidationError} If validation fails
|
|
94
|
+
*/
|
|
95
|
+
export function validateObject(obj, schema, context = '') {
|
|
96
|
+
validateType(obj, 'object', 'object', context);
|
|
97
|
+
validateType(schema, 'object', 'schema', context);
|
|
98
|
+
|
|
99
|
+
const errors = [];
|
|
100
|
+
|
|
101
|
+
// Check required fields
|
|
102
|
+
if (schema.required && Array.isArray(schema.required)) {
|
|
103
|
+
for (const field of schema.required) {
|
|
104
|
+
if (obj[field] === undefined || obj[field] === null) {
|
|
105
|
+
errors.push({
|
|
106
|
+
field,
|
|
107
|
+
message: 'Required field is missing'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check field types and constraints
|
|
114
|
+
if (schema.properties && typeof schema.properties === 'object') {
|
|
115
|
+
for (const [field, fieldSchema] of Object.entries(schema.properties)) {
|
|
116
|
+
// Skip if field is not present and not required
|
|
117
|
+
if ((obj[field] === undefined || obj[field] === null) &&
|
|
118
|
+
(!schema.required || !schema.required.includes(field))) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Validate field if present
|
|
123
|
+
if (obj[field] !== undefined && obj[field] !== null) {
|
|
124
|
+
// Check type
|
|
125
|
+
if (fieldSchema.type) {
|
|
126
|
+
try {
|
|
127
|
+
validateType(obj[field], fieldSchema.type, field, context);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (error instanceof ValidationError) {
|
|
130
|
+
errors.push(...error.errors);
|
|
131
|
+
} else {
|
|
132
|
+
errors.push({ field, message: error.message });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check pattern for strings
|
|
138
|
+
if (fieldSchema.type === 'string' && fieldSchema.pattern) {
|
|
139
|
+
try {
|
|
140
|
+
validatePattern(obj[field], new RegExp(fieldSchema.pattern), field, context);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (error instanceof ValidationError) {
|
|
143
|
+
errors.push(...error.errors);
|
|
144
|
+
} else {
|
|
145
|
+
errors.push({ field, message: error.message });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Check enum values
|
|
151
|
+
if (fieldSchema.enum && Array.isArray(fieldSchema.enum) &&
|
|
152
|
+
!fieldSchema.enum.includes(obj[field])) {
|
|
153
|
+
errors.push({
|
|
154
|
+
field,
|
|
155
|
+
message: `Value must be one of: ${fieldSchema.enum.join(', ')}`,
|
|
156
|
+
enum: fieldSchema.enum,
|
|
157
|
+
actual: obj[field]
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Check nested objects recursively
|
|
162
|
+
if (fieldSchema.type === 'object' && fieldSchema.properties &&
|
|
163
|
+
typeof obj[field] === 'object' && obj[field] !== null) {
|
|
164
|
+
try {
|
|
165
|
+
validateObject(obj[field], fieldSchema, `${context}.${field}`);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof ValidationError) {
|
|
168
|
+
// Add parent field to nested errors
|
|
169
|
+
const nestedErrors = error.errors.map(err => ({
|
|
170
|
+
...err,
|
|
171
|
+
field: `${field}.${err.field}`
|
|
172
|
+
}));
|
|
173
|
+
errors.push(...nestedErrors);
|
|
174
|
+
} else {
|
|
175
|
+
errors.push({ field, message: error.message });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check array items
|
|
181
|
+
if (fieldSchema.type === 'array' && fieldSchema.items &&
|
|
182
|
+
Array.isArray(obj[field])) {
|
|
183
|
+
for (let i = 0; i < obj[field].length; i++) {
|
|
184
|
+
const item = obj[field][i];
|
|
185
|
+
|
|
186
|
+
// Validate item type
|
|
187
|
+
if (typeof fieldSchema.items === 'string') {
|
|
188
|
+
try {
|
|
189
|
+
validateType(item, fieldSchema.items, `${field}[${i}]`, context);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (error instanceof ValidationError) {
|
|
192
|
+
errors.push(...error.errors);
|
|
193
|
+
} else {
|
|
194
|
+
errors.push({ field: `${field}[${i}]`, message: error.message });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Validate item against schema
|
|
199
|
+
else if (typeof fieldSchema.items === 'object') {
|
|
200
|
+
try {
|
|
201
|
+
validateObject(item, fieldSchema.items, `${context}.${field}[${i}]`);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (error instanceof ValidationError) {
|
|
204
|
+
// Add parent field to nested errors
|
|
205
|
+
const nestedErrors = error.errors.map(err => ({
|
|
206
|
+
...err,
|
|
207
|
+
field: `${field}[${i}].${err.field}`
|
|
208
|
+
}));
|
|
209
|
+
errors.push(...nestedErrors);
|
|
210
|
+
} else {
|
|
211
|
+
errors.push({ field: `${field}[${i}]`, message: error.message });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// If errors were found, throw ValidationError
|
|
222
|
+
if (errors.length > 0) {
|
|
223
|
+
const errorMsg = `Validation failed for ${context || 'object'}: ${errors.map(e => e.message).join(', ')}`;
|
|
224
|
+
logger.error(errorMsg, { errors });
|
|
225
|
+
throw new ValidationError(errorMsg, errors);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Validates tool input against its schema
|
|
233
|
+
* @param {Object} toolSchema - Tool schema with parameters definition
|
|
234
|
+
* @param {Object} input - Input data to validate
|
|
235
|
+
* @returns {Object} Validation result with success boolean and optional errors
|
|
236
|
+
*/
|
|
237
|
+
export function validateToolInput(toolSchema, input) {
|
|
238
|
+
if (!toolSchema || !toolSchema.parameters) {
|
|
239
|
+
return { valid: true };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
validateObject(input, toolSchema.parameters, `tool.${toolSchema.name || 'unknown'}`);
|
|
244
|
+
return { valid: true };
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (error instanceof ValidationError) {
|
|
247
|
+
return {
|
|
248
|
+
valid: false,
|
|
249
|
+
errors: error.errors
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
valid: false,
|
|
255
|
+
errors: [{ message: error.message }]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Validates that a value is one of the allowed values
|
|
262
|
+
* @param {any} value - Value to check
|
|
263
|
+
* @param {Array} allowedValues - List of allowed values
|
|
264
|
+
* @param {string} name - Name of the value for error messages
|
|
265
|
+
* @param {string} context - Context for logging
|
|
266
|
+
* @throws {ValidationError} If validation fails
|
|
267
|
+
*/
|
|
268
|
+
export function validateEnum(value, allowedValues, name, context = '') {
|
|
269
|
+
if (value === undefined || value === null) {
|
|
270
|
+
return; // Skip enum validation for null/undefined
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!Array.isArray(allowedValues)) {
|
|
274
|
+
throw new Error('allowedValues must be an array');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!allowedValues.includes(value)) {
|
|
278
|
+
const errorMsg = `${name} must be one of: ${allowedValues.join(', ')}`;
|
|
279
|
+
logger.error(errorMsg, { context, value });
|
|
280
|
+
throw new ValidationError(errorMsg, [{
|
|
281
|
+
field: name,
|
|
282
|
+
message: 'Value is not in allowed values list',
|
|
283
|
+
allowedValues,
|
|
284
|
+
actual: value
|
|
285
|
+
}]);
|
|
286
|
+
}
|
|
287
|
+
}
|