@ordis-dev/ordis 0.2.0 → 0.4.0

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 (75) hide show
  1. package/README.md +47 -0
  2. package/dist/cli.js +20 -4
  3. package/dist/cli.js.map +1 -1
  4. package/dist/core/error-formatter.d.ts +35 -0
  5. package/dist/core/error-formatter.d.ts.map +1 -0
  6. package/dist/core/error-formatter.js +319 -0
  7. package/dist/core/error-formatter.js.map +1 -0
  8. package/dist/core/pipeline.d.ts.map +1 -1
  9. package/dist/core/pipeline.js +3 -2
  10. package/dist/core/pipeline.js.map +1 -1
  11. package/dist/core/types.d.ts +2 -1
  12. package/dist/core/types.d.ts.map +1 -1
  13. package/dist/core/validator.d.ts +3 -1
  14. package/dist/core/validator.d.ts.map +1 -1
  15. package/dist/core/validator.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/llm/client.d.ts.map +1 -1
  21. package/dist/llm/client.js +23 -3
  22. package/dist/llm/client.js.map +1 -1
  23. package/dist/llm/types.d.ts +2 -0
  24. package/dist/llm/types.d.ts.map +1 -1
  25. package/dist/schemas/types.d.ts +3 -1
  26. package/dist/schemas/types.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/dist/__tests__/api.test.d.ts +0 -5
  29. package/dist/__tests__/api.test.d.ts.map +0 -1
  30. package/dist/__tests__/api.test.js +0 -95
  31. package/dist/__tests__/api.test.js.map +0 -1
  32. package/dist/__tests__/cli.test.d.ts +0 -6
  33. package/dist/__tests__/cli.test.d.ts.map +0 -1
  34. package/dist/__tests__/cli.test.js +0 -103
  35. package/dist/__tests__/cli.test.js.map +0 -1
  36. package/dist/cli/__tests__/cli.test.d.ts +0 -5
  37. package/dist/cli/__tests__/cli.test.d.ts.map +0 -1
  38. package/dist/cli/__tests__/cli.test.js +0 -13
  39. package/dist/cli/__tests__/cli.test.js.map +0 -1
  40. package/dist/core/__tests__/pipeline.test.d.ts +0 -5
  41. package/dist/core/__tests__/pipeline.test.d.ts.map +0 -1
  42. package/dist/core/__tests__/pipeline.test.js +0 -334
  43. package/dist/core/__tests__/pipeline.test.js.map +0 -1
  44. package/dist/core/__tests__/validator.test.d.ts +0 -5
  45. package/dist/core/__tests__/validator.test.d.ts.map +0 -1
  46. package/dist/core/__tests__/validator.test.js +0 -124
  47. package/dist/core/__tests__/validator.test.js.map +0 -1
  48. package/dist/llm/__tests__/client.test.d.ts +0 -5
  49. package/dist/llm/__tests__/client.test.d.ts.map +0 -1
  50. package/dist/llm/__tests__/client.test.js +0 -350
  51. package/dist/llm/__tests__/client.test.js.map +0 -1
  52. package/dist/llm/__tests__/prompt-builder.test.d.ts +0 -5
  53. package/dist/llm/__tests__/prompt-builder.test.d.ts.map +0 -1
  54. package/dist/llm/__tests__/prompt-builder.test.js +0 -171
  55. package/dist/llm/__tests__/prompt-builder.test.js.map +0 -1
  56. package/dist/llm/__tests__/retry.test.d.ts +0 -5
  57. package/dist/llm/__tests__/retry.test.d.ts.map +0 -1
  58. package/dist/llm/__tests__/retry.test.js +0 -350
  59. package/dist/llm/__tests__/retry.test.js.map +0 -1
  60. package/dist/llm/__tests__/token-counter.test.d.ts +0 -5
  61. package/dist/llm/__tests__/token-counter.test.d.ts.map +0 -1
  62. package/dist/llm/__tests__/token-counter.test.js +0 -166
  63. package/dist/llm/__tests__/token-counter.test.js.map +0 -1
  64. package/dist/schemas/__tests__/integration.test.d.ts +0 -5
  65. package/dist/schemas/__tests__/integration.test.d.ts.map +0 -1
  66. package/dist/schemas/__tests__/integration.test.js +0 -366
  67. package/dist/schemas/__tests__/integration.test.js.map +0 -1
  68. package/dist/schemas/__tests__/loader.test.d.ts +0 -5
  69. package/dist/schemas/__tests__/loader.test.d.ts.map +0 -1
  70. package/dist/schemas/__tests__/loader.test.js +0 -271
  71. package/dist/schemas/__tests__/loader.test.js.map +0 -1
  72. package/dist/schemas/__tests__/validator.test.d.ts +0 -5
  73. package/dist/schemas/__tests__/validator.test.d.ts.map +0 -1
  74. package/dist/schemas/__tests__/validator.test.js +0 -592
  75. package/dist/schemas/__tests__/validator.test.js.map +0 -1
@@ -1,366 +0,0 @@
1
- /**
2
- * Integration tests for schema system
3
- */
4
- import { describe, it, expect } from 'vitest';
5
- import { parseSchema } from '../loader.js';
6
- import { SchemaValidationError, ErrorCodes } from '../errors.js';
7
- describe('Schema System Integration', () => {
8
- describe('End-to-end validation', () => {
9
- it('should successfully parse and validate complete invoice schema', () => {
10
- const schemaJson = `{
11
- "fields": {
12
- "invoice_id": {
13
- "type": "string",
14
- "description": "Unique invoice identifier"
15
- },
16
- "amount": {
17
- "type": "number",
18
- "description": "Total invoice amount"
19
- },
20
- "currency": {
21
- "type": "enum",
22
- "enum": ["USD", "SGD", "EUR"],
23
- "description": "Currency code"
24
- },
25
- "date": {
26
- "type": "date",
27
- "optional": true,
28
- "description": "Invoice date"
29
- }
30
- }
31
- }`;
32
- const schema = parseSchema(schemaJson);
33
- expect(schema.fields).toBeDefined();
34
- expect(Object.keys(schema.fields)).toHaveLength(4);
35
- expect(schema.fields.invoice_id.type).toBe('string');
36
- expect(schema.fields.amount.type).toBe('number');
37
- expect(schema.fields.currency.enum).toEqual(['USD', 'SGD', 'EUR']);
38
- expect(schema.fields.date.optional).toBe(true);
39
- });
40
- it('should catch multiple validation errors in order', () => {
41
- // Test that validation stops at first error (fail-fast approach)
42
- const invalidSchemas = [
43
- '{ "notFields": {} }', // Missing fields
44
- '{ "fields": {} }', // Empty fields
45
- '{ "fields": { "": { "type": "string" } } }', // Empty field name
46
- '{ "fields": { "123": { "type": "string" } } }', // Invalid field name
47
- '{ "fields": { "name": {} } }', // Missing type
48
- '{ "fields": { "name": { "type": "invalid" } } }', // Invalid type
49
- ];
50
- invalidSchemas.forEach((schemaJson) => {
51
- expect(() => parseSchema(schemaJson)).toThrow(SchemaValidationError);
52
- });
53
- });
54
- it('should validate complex nested constraints', () => {
55
- const schemaJson = `{
56
- "fields": {
57
- "price": {
58
- "type": "number",
59
- "min": 0.01,
60
- "max": 999999.99
61
- },
62
- "status": {
63
- "type": "enum",
64
- "enum": ["draft", "pending", "approved", "rejected", "archived"]
65
- },
66
- "email": {
67
- "type": "string",
68
- "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$"
69
- }
70
- },
71
- "metadata": {
72
- "name": "Order Schema",
73
- "version": "3.1.0",
74
- "description": "Schema for order processing"
75
- }
76
- }`;
77
- const schema = parseSchema(schemaJson);
78
- expect(schema.fields.price.min).toBe(0.01);
79
- expect(schema.fields.price.max).toBe(999999.99);
80
- expect(schema.fields.status.enum).toHaveLength(5);
81
- expect(schema.fields.email.pattern).toBeDefined();
82
- expect(schema.metadata?.version).toBe('3.1.0');
83
- });
84
- });
85
- describe('Error reporting quality', () => {
86
- it('should provide clear error for missing fields property', () => {
87
- const schemaJson = '{ "metadata": { "name": "Test" } }';
88
- try {
89
- parseSchema(schemaJson);
90
- expect.fail('Should have thrown');
91
- }
92
- catch (error) {
93
- expect(error).toBeInstanceOf(SchemaValidationError);
94
- const err = error;
95
- expect(err.message).toContain('fields');
96
- expect(err.code).toBe(ErrorCodes.MISSING_FIELDS);
97
- }
98
- });
99
- it('should provide clear error for invalid field type', () => {
100
- const schemaJson = '{ "fields": { "name": { "type": "text" } } }';
101
- try {
102
- parseSchema(schemaJson);
103
- expect.fail('Should have thrown');
104
- }
105
- catch (error) {
106
- expect(error).toBeInstanceOf(SchemaValidationError);
107
- const err = error;
108
- expect(err.message).toContain('invalid type');
109
- expect(err.message).toContain('text');
110
- expect(err.field).toBe('name');
111
- expect(err.code).toBe(ErrorCodes.INVALID_FIELD_TYPE);
112
- expect(err.details?.validTypes).toEqual(['string', 'number', 'date', 'enum', 'boolean']);
113
- }
114
- });
115
- it('should provide clear error for enum without values', () => {
116
- const schemaJson = '{ "fields": { "status": { "type": "enum" } } }';
117
- try {
118
- parseSchema(schemaJson);
119
- expect.fail('Should have thrown');
120
- }
121
- catch (error) {
122
- expect(error).toBeInstanceOf(SchemaValidationError);
123
- const err = error;
124
- expect(err.message).toContain("must have an 'enum' property");
125
- expect(err.field).toBe('status');
126
- expect(err.code).toBe(ErrorCodes.MISSING_ENUM_VALUES);
127
- }
128
- });
129
- it('should provide clear error for constraint mismatch', () => {
130
- const schemaJson = '{ "fields": { "age": { "type": "number", "min": 100, "max": 50 } } }';
131
- try {
132
- parseSchema(schemaJson);
133
- expect.fail('Should have thrown');
134
- }
135
- catch (error) {
136
- expect(error).toBeInstanceOf(SchemaValidationError);
137
- const err = error;
138
- expect(err.message).toContain('cannot be greater than');
139
- expect(err.field).toBe('age');
140
- expect(err.code).toBe(ErrorCodes.CONSTRAINT_MISMATCH);
141
- expect(err.details?.min).toBe(100);
142
- expect(err.details?.max).toBe(50);
143
- }
144
- });
145
- });
146
- describe('Realistic use cases', () => {
147
- it('should handle product catalog schema', () => {
148
- const schema = parseSchema(`{
149
- "fields": {
150
- "sku": {
151
- "type": "string",
152
- "pattern": "^[A-Z]{3}-[0-9]{6}$",
153
- "description": "Stock Keeping Unit"
154
- },
155
- "name": {
156
- "type": "string",
157
- "description": "Product name"
158
- },
159
- "price": {
160
- "type": "number",
161
- "min": 0,
162
- "description": "Price in cents"
163
- },
164
- "category": {
165
- "type": "enum",
166
- "enum": ["electronics", "clothing", "books", "food"],
167
- "description": "Product category"
168
- },
169
- "in_stock": {
170
- "type": "enum",
171
- "enum": ["yes", "no"],
172
- "description": "Stock availability"
173
- },
174
- "discontinued_date": {
175
- "type": "date",
176
- "optional": true,
177
- "description": "Date when product was discontinued"
178
- }
179
- },
180
- "metadata": {
181
- "name": "Product Catalog",
182
- "version": "1.0.0"
183
- }
184
- }`);
185
- expect(schema.fields.sku.pattern).toBe('^[A-Z]{3}-[0-9]{6}$');
186
- expect(schema.fields.category.enum).toHaveLength(4);
187
- expect(schema.fields.discontinued_date.optional).toBe(true);
188
- });
189
- it('should handle event registration schema', () => {
190
- const schema = parseSchema(`{
191
- "fields": {
192
- "event_name": {
193
- "type": "string"
194
- },
195
- "attendee_count": {
196
- "type": "number",
197
- "min": 1,
198
- "max": 10000
199
- },
200
- "event_type": {
201
- "type": "enum",
202
- "enum": ["conference", "workshop", "webinar", "meetup"]
203
- },
204
- "event_date": {
205
- "type": "date"
206
- },
207
- "registration_email": {
208
- "type": "string",
209
- "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$"
210
- },
211
- "notes": {
212
- "type": "string",
213
- "optional": true
214
- }
215
- }
216
- }`);
217
- expect(schema.fields.attendee_count.min).toBe(1);
218
- expect(schema.fields.attendee_count.max).toBe(10000);
219
- expect(schema.fields.event_type.enum).toContain('workshop');
220
- expect(schema.fields.notes.optional).toBe(true);
221
- });
222
- });
223
- describe('Edge cases', () => {
224
- it('should handle schema with only one field', () => {
225
- const schema = parseSchema(`{
226
- "fields": {
227
- "id": { "type": "string" }
228
- }
229
- }`);
230
- expect(Object.keys(schema.fields)).toHaveLength(1);
231
- });
232
- it('should handle fields with all optional properties', () => {
233
- const schema = parseSchema(`{
234
- "fields": {
235
- "field1": {
236
- "type": "string",
237
- "description": "A field",
238
- "optional": true
239
- }
240
- }
241
- }`);
242
- expect(schema.fields.field1.optional).toBe(true);
243
- expect(schema.fields.field1.description).toBe('A field');
244
- });
245
- it('should handle minimal valid schema', () => {
246
- const schema = parseSchema(`{
247
- "fields": {
248
- "x": { "type": "string" }
249
- }
250
- }`);
251
- expect(schema.fields.x.type).toBe('string');
252
- });
253
- it('should handle schema with metadata only', () => {
254
- const schema = parseSchema(`{
255
- "fields": {
256
- "id": { "type": "string" }
257
- },
258
- "metadata": {
259
- "name": "Simple Schema"
260
- }
261
- }`);
262
- expect(schema.metadata?.name).toBe('Simple Schema');
263
- expect(schema.metadata?.version).toBeUndefined();
264
- expect(schema.metadata?.description).toBeUndefined();
265
- });
266
- });
267
- describe('Confidence Configuration', () => {
268
- it('should parse schema with strict confidence requirements', () => {
269
- const schema = parseSchema(`{
270
- "fields": {
271
- "transaction_id": { "type": "string" },
272
- "amount": { "type": "number" }
273
- },
274
- "confidence": {
275
- "threshold": 95,
276
- "failOnLowConfidence": true
277
- },
278
- "metadata": {
279
- "name": "Financial Transaction",
280
- "description": "High-confidence extraction for financial data"
281
- }
282
- }`);
283
- expect(schema.confidence?.threshold).toBe(95);
284
- expect(schema.confidence?.failOnLowConfidence).toBe(true);
285
- });
286
- it('should parse schema with permissive confidence settings', () => {
287
- const schema = parseSchema(`{
288
- "fields": {
289
- "title": { "type": "string" },
290
- "tags": { "type": "string", "optional": true }
291
- },
292
- "confidence": {
293
- "threshold": 60,
294
- "failOnLowConfidence": false
295
- }
296
- }`);
297
- expect(schema.confidence?.threshold).toBe(60);
298
- expect(schema.confidence?.failOnLowConfidence).toBe(false);
299
- });
300
- it('should allow schema without confidence config', () => {
301
- const schema = parseSchema(`{
302
- "fields": {
303
- "name": { "type": "string" }
304
- }
305
- }`);
306
- expect(schema.confidence).toBeUndefined();
307
- });
308
- it('should reject schema with invalid confidence threshold', () => {
309
- const invalidSchema = `{
310
- "fields": {
311
- "name": { "type": "string" }
312
- },
313
- "confidence": {
314
- "threshold": 150,
315
- "failOnLowConfidence": true
316
- }
317
- }`;
318
- expect(() => parseSchema(invalidSchema)).toThrow(SchemaValidationError);
319
- expect(() => parseSchema(invalidSchema)).toThrow(/between 0 and 100/);
320
- });
321
- it('should reject schema with incomplete confidence config', () => {
322
- const incompleteSchema = `{
323
- "fields": {
324
- "name": { "type": "string" }
325
- },
326
- "confidence": {
327
- "threshold": 80
328
- }
329
- }`;
330
- expect(() => parseSchema(incompleteSchema)).toThrow(SchemaValidationError);
331
- expect(() => parseSchema(incompleteSchema)).toThrow(/failOnLowConfidence/);
332
- });
333
- it('should handle real-world high-stakes schema', () => {
334
- const schema = parseSchema(`{
335
- "fields": {
336
- "invoice_number": {
337
- "type": "string",
338
- "pattern": "^INV-[0-9]{6}$"
339
- },
340
- "total_amount": {
341
- "type": "number",
342
- "min": 0
343
- },
344
- "payment_status": {
345
- "type": "enum",
346
- "enum": ["paid", "pending", "overdue"]
347
- }
348
- },
349
- "confidence": {
350
- "threshold": 90,
351
- "failOnLowConfidence": true
352
- },
353
- "metadata": {
354
- "name": "Invoice Extraction",
355
- "version": "2.0.0",
356
- "description": "Requires 90% confidence for automated processing"
357
- }
358
- }`);
359
- expect(schema.fields.invoice_number.pattern).toBeDefined();
360
- expect(schema.confidence?.threshold).toBe(90);
361
- expect(schema.confidence?.failOnLowConfidence).toBe(true);
362
- expect(schema.metadata?.version).toBe('2.0.0');
363
- });
364
- });
365
- });
366
- //# sourceMappingURL=integration.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../../../src/schemas/__tests__/integration.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +0,0 @@
1
- /**
2
- * Unit tests for schema loader
3
- */
4
- export {};
5
- //# sourceMappingURL=loader.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loader.test.d.ts","sourceRoot":"","sources":["../../../src/schemas/__tests__/loader.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -1,271 +0,0 @@
1
- /**
2
- * Unit tests for schema loader
3
- */
4
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
5
- import * as fs from 'fs/promises';
6
- import * as path from 'path';
7
- import { loadSchema, parseSchema, loadSchemaFromObject } from '../loader.js';
8
- import { SchemaValidationError, ErrorCodes } from '../errors.js';
9
- const TEST_DIR = path.join(process.cwd(), 'test-schemas');
10
- describe('Schema Loader', () => {
11
- beforeEach(async () => {
12
- // Create test directory
13
- await fs.mkdir(TEST_DIR, { recursive: true });
14
- });
15
- afterEach(async () => {
16
- // Clean up test directory
17
- await fs.rm(TEST_DIR, { recursive: true, force: true });
18
- });
19
- describe('loadSchema', () => {
20
- it('should load a valid schema from file', async () => {
21
- const schema = {
22
- fields: {
23
- name: { type: 'string' },
24
- age: { type: 'number' },
25
- },
26
- };
27
- const filePath = path.join(TEST_DIR, 'valid.schema.json');
28
- await fs.writeFile(filePath, JSON.stringify(schema));
29
- const loaded = await loadSchema(filePath);
30
- expect(loaded).toEqual(schema);
31
- });
32
- it('should throw error for non-existent file', async () => {
33
- const filePath = path.join(TEST_DIR, 'does-not-exist.json');
34
- await expect(loadSchema(filePath)).rejects.toThrow(SchemaValidationError);
35
- await expect(loadSchema(filePath)).rejects.toThrow(/Cannot read schema file/);
36
- });
37
- it('should throw error for invalid JSON', async () => {
38
- const filePath = path.join(TEST_DIR, 'invalid.json');
39
- await fs.writeFile(filePath, '{ invalid json }');
40
- await expect(loadSchema(filePath)).rejects.toThrow(SchemaValidationError);
41
- await expect(loadSchema(filePath)).rejects.toThrow(/Invalid JSON/);
42
- });
43
- it('should throw error for invalid schema structure', async () => {
44
- const invalidSchema = {
45
- // Missing fields property
46
- metadata: { name: 'Test' },
47
- };
48
- const filePath = path.join(TEST_DIR, 'invalid-schema.json');
49
- await fs.writeFile(filePath, JSON.stringify(invalidSchema));
50
- await expect(loadSchema(filePath)).rejects.toThrow(SchemaValidationError);
51
- });
52
- it('should load schema with metadata', async () => {
53
- const schema = {
54
- fields: {
55
- id: { type: 'string' },
56
- },
57
- metadata: {
58
- name: 'Test Schema',
59
- version: '1.0.0',
60
- description: 'A test schema',
61
- },
62
- };
63
- const filePath = path.join(TEST_DIR, 'with-metadata.json');
64
- await fs.writeFile(filePath, JSON.stringify(schema));
65
- const loaded = await loadSchema(filePath);
66
- expect(loaded).toEqual(schema);
67
- expect(loaded.metadata?.name).toBe('Test Schema');
68
- });
69
- it('should load complex schema with all field types', async () => {
70
- const schema = {
71
- fields: {
72
- invoice_id: { type: 'string', description: 'Invoice ID' },
73
- amount: { type: 'number', min: 0 },
74
- currency: { type: 'enum', enum: ['USD', 'EUR', 'GBP'] },
75
- date: { type: 'date', optional: true },
76
- email: { type: 'string', pattern: '^[a-z]+@[a-z]+\\.[a-z]+$' },
77
- },
78
- };
79
- const filePath = path.join(TEST_DIR, 'complex.json');
80
- await fs.writeFile(filePath, JSON.stringify(schema, null, 2));
81
- const loaded = await loadSchema(filePath);
82
- expect(loaded).toEqual(schema);
83
- });
84
- });
85
- describe('parseSchema', () => {
86
- it('should parse valid JSON string', () => {
87
- const schemaString = JSON.stringify({
88
- fields: {
89
- name: { type: 'string' },
90
- },
91
- });
92
- const schema = parseSchema(schemaString);
93
- expect(schema.fields.name.type).toBe('string');
94
- });
95
- it('should throw error for invalid JSON string', () => {
96
- const invalidJson = '{ invalid }';
97
- expect(() => parseSchema(invalidJson)).toThrow(SchemaValidationError);
98
- expect(() => parseSchema(invalidJson)).toThrow(/Invalid JSON/);
99
- });
100
- it('should throw error for invalid schema structure', () => {
101
- const invalidSchema = JSON.stringify({ notFields: {} });
102
- expect(() => parseSchema(invalidSchema)).toThrow(SchemaValidationError);
103
- });
104
- it('should parse schema with all features', () => {
105
- const schemaString = JSON.stringify({
106
- fields: {
107
- id: { type: 'string' },
108
- count: { type: 'number', min: 0, max: 100 },
109
- status: { type: 'enum', enum: ['active', 'inactive'] },
110
- created: { type: 'date', optional: true },
111
- },
112
- metadata: {
113
- name: 'Complete Schema',
114
- version: '1.0.0',
115
- },
116
- });
117
- const schema = parseSchema(schemaString);
118
- expect(Object.keys(schema.fields)).toHaveLength(4);
119
- expect(schema.metadata?.name).toBe('Complete Schema');
120
- });
121
- });
122
- describe('loadSchemaFromObject', () => {
123
- it('should load valid schema object', () => {
124
- const schemaObj = {
125
- fields: {
126
- title: { type: 'string' },
127
- },
128
- };
129
- const schema = loadSchemaFromObject(schemaObj);
130
- expect(schema).toEqual(schemaObj);
131
- });
132
- it('should throw error for invalid schema object', () => {
133
- const invalidObj = {
134
- notFields: { title: { type: 'string' } },
135
- };
136
- expect(() => loadSchemaFromObject(invalidObj)).toThrow(SchemaValidationError);
137
- });
138
- it('should validate all constraints', () => {
139
- const invalidObj = {
140
- fields: {
141
- age: { type: 'number', min: 100, max: 50 }, // min > max
142
- },
143
- };
144
- expect(() => loadSchemaFromObject(invalidObj)).toThrow(SchemaValidationError);
145
- });
146
- it('should accept schema with metadata', () => {
147
- const schemaObj = {
148
- fields: {
149
- name: { type: 'string' },
150
- },
151
- metadata: {
152
- name: 'Object Schema',
153
- },
154
- };
155
- const schema = loadSchemaFromObject(schemaObj);
156
- expect(schema.metadata?.name).toBe('Object Schema');
157
- });
158
- });
159
- describe('Error handling', () => {
160
- it('should provide file path in error details', async () => {
161
- const filePath = path.join(TEST_DIR, 'bad-json.json');
162
- await fs.writeFile(filePath, '{ bad json');
163
- try {
164
- await loadSchema(filePath);
165
- expect.fail('Should have thrown an error');
166
- }
167
- catch (error) {
168
- expect(error).toBeInstanceOf(SchemaValidationError);
169
- const schemaError = error;
170
- expect(schemaError.details?.filePath).toBe(filePath);
171
- }
172
- });
173
- it('should have appropriate error code for JSON errors', async () => {
174
- const filePath = path.join(TEST_DIR, 'bad.json');
175
- await fs.writeFile(filePath, 'not json at all');
176
- try {
177
- await loadSchema(filePath);
178
- expect.fail('Should have thrown an error');
179
- }
180
- catch (error) {
181
- expect(error).toBeInstanceOf(SchemaValidationError);
182
- expect(error.code).toBe(ErrorCodes.INVALID_JSON);
183
- }
184
- });
185
- it('should handle read permission errors gracefully', async () => {
186
- const filePath = path.join(TEST_DIR, 'readonly.json');
187
- await fs.writeFile(filePath, '{}');
188
- await fs.chmod(filePath, 0o000); // Remove all permissions
189
- try {
190
- await expect(loadSchema(filePath)).rejects.toThrow(SchemaValidationError);
191
- }
192
- finally {
193
- // Restore permissions for cleanup
194
- await fs.chmod(filePath, 0o644);
195
- }
196
- });
197
- });
198
- describe('Real-world schemas', () => {
199
- it('should load invoice schema', async () => {
200
- const invoiceSchema = {
201
- fields: {
202
- invoice_id: {
203
- type: 'string',
204
- description: 'Unique invoice identifier',
205
- },
206
- amount: {
207
- type: 'number',
208
- description: 'Total invoice amount',
209
- },
210
- currency: {
211
- type: 'enum',
212
- enum: ['USD', 'SGD', 'EUR'],
213
- description: 'Currency code',
214
- },
215
- date: {
216
- type: 'date',
217
- optional: true,
218
- description: 'Invoice date',
219
- },
220
- },
221
- };
222
- const filePath = path.join(TEST_DIR, 'invoice.schema.json');
223
- await fs.writeFile(filePath, JSON.stringify(invoiceSchema, null, 2));
224
- const loaded = await loadSchema(filePath);
225
- expect(loaded.fields.invoice_id.type).toBe('string');
226
- expect(loaded.fields.amount.type).toBe('number');
227
- expect(loaded.fields.currency.type).toBe('enum');
228
- expect(loaded.fields.currency.enum).toEqual(['USD', 'SGD', 'EUR']);
229
- expect(loaded.fields.date.optional).toBe(true);
230
- });
231
- it('should load user profile schema', async () => {
232
- const userSchema = {
233
- fields: {
234
- username: {
235
- type: 'string',
236
- pattern: '^[a-zA-Z0-9_]{3,20}$',
237
- },
238
- email: {
239
- type: 'string',
240
- pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
241
- },
242
- age: {
243
- type: 'number',
244
- min: 13,
245
- max: 120,
246
- },
247
- role: {
248
- type: 'enum',
249
- enum: ['user', 'admin', 'moderator'],
250
- },
251
- bio: {
252
- type: 'string',
253
- optional: true,
254
- },
255
- },
256
- metadata: {
257
- name: 'User Profile',
258
- version: '2.0.0',
259
- description: 'Schema for user profile data',
260
- },
261
- };
262
- const filePath = path.join(TEST_DIR, 'user.schema.json');
263
- await fs.writeFile(filePath, JSON.stringify(userSchema, null, 2));
264
- const loaded = await loadSchema(filePath);
265
- expect(loaded.fields.username.pattern).toBeDefined();
266
- expect(loaded.fields.age.min).toBe(13);
267
- expect(loaded.metadata?.version).toBe('2.0.0');
268
- });
269
- });
270
- });
271
- //# sourceMappingURL=loader.test.js.map