@trayio/commons 5.12.0 → 5.13.0-unstable
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/dist/schema/JsonSchemaIntrospector.d.ts +53 -0
- package/dist/schema/JsonSchemaIntrospector.d.ts.map +1 -0
- package/dist/schema/JsonSchemaIntrospector.js +118 -0
- package/dist/schema/JsonSchemaIntrospector.unit.test.d.ts +2 -0
- package/dist/schema/JsonSchemaIntrospector.unit.test.d.ts.map +1 -0
- package/dist/schema/JsonSchemaIntrospector.unit.test.js +169 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +5 -0
- package/package.json +3 -2
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for converting runtime JavaScript objects to JSON Schema
|
|
3
|
+
* Used for dynamic output schema generation in CDK connectors
|
|
4
|
+
*/
|
|
5
|
+
export declare class JsonSchemaIntrospector {
|
|
6
|
+
/**
|
|
7
|
+
* Introspects a JavaScript object and generates a JSON Schema representation
|
|
8
|
+
*
|
|
9
|
+
* @param obj - The object to introspect
|
|
10
|
+
* @param options - Configuration options for introspection
|
|
11
|
+
* @returns A JSON Schema object describing the structure
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const data = { id: 1, name: "John", active: true };
|
|
16
|
+
* const schema = JsonSchemaIntrospector.introspectToJsonSchema(data);
|
|
17
|
+
* // Returns:
|
|
18
|
+
* // {
|
|
19
|
+
* // type: 'object',
|
|
20
|
+
* // properties: {
|
|
21
|
+
* // id: { type: 'number' },
|
|
22
|
+
* // name: { type: 'string' },
|
|
23
|
+
* // active: { type: 'boolean' }
|
|
24
|
+
* // },
|
|
25
|
+
* // required: ['id', 'name', 'active']
|
|
26
|
+
* // }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
static introspectToJsonSchema(obj: any, options?: IntrospectionOptions): any;
|
|
30
|
+
private static introspectRecursive;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Configuration options for JSON Schema introspection
|
|
34
|
+
*/
|
|
35
|
+
export type IntrospectionOptions = {
|
|
36
|
+
/**
|
|
37
|
+
* Maximum depth for recursive introspection
|
|
38
|
+
* @default 10
|
|
39
|
+
*/
|
|
40
|
+
maxDepth?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to include example values in the schema
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
includeExamples?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Whether to mark all present fields as required
|
|
48
|
+
* If false, only marks non-null/non-undefined fields as required
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
strictRequired?: boolean;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=JsonSchemaIntrospector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonSchemaIntrospector.d.ts","sourceRoot":"","sources":["../../src/schema/JsonSchemaIntrospector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,sBAAsB;IAClC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAC,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,GAAG;IAU5E,OAAO,CAAC,MAAM,CAAC,mBAAmB;CA8FlC;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JsonSchemaIntrospector = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Utility for converting runtime JavaScript objects to JSON Schema
|
|
6
|
+
* Used for dynamic output schema generation in CDK connectors
|
|
7
|
+
*/
|
|
8
|
+
class JsonSchemaIntrospector {
|
|
9
|
+
/**
|
|
10
|
+
* Introspects a JavaScript object and generates a JSON Schema representation
|
|
11
|
+
*
|
|
12
|
+
* @param obj - The object to introspect
|
|
13
|
+
* @param options - Configuration options for introspection
|
|
14
|
+
* @returns A JSON Schema object describing the structure
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const data = { id: 1, name: "John", active: true };
|
|
19
|
+
* const schema = JsonSchemaIntrospector.introspectToJsonSchema(data);
|
|
20
|
+
* // Returns:
|
|
21
|
+
* // {
|
|
22
|
+
* // type: 'object',
|
|
23
|
+
* // properties: {
|
|
24
|
+
* // id: { type: 'number' },
|
|
25
|
+
* // name: { type: 'string' },
|
|
26
|
+
* // active: { type: 'boolean' }
|
|
27
|
+
* // },
|
|
28
|
+
* // required: ['id', 'name', 'active']
|
|
29
|
+
* // }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
static introspectToJsonSchema(obj, options) {
|
|
33
|
+
const opts = {
|
|
34
|
+
maxDepth: options?.maxDepth ?? 10,
|
|
35
|
+
includeExamples: options?.includeExamples ?? false,
|
|
36
|
+
strictRequired: options?.strictRequired ?? true,
|
|
37
|
+
};
|
|
38
|
+
return this.introspectRecursive(obj, 0, opts);
|
|
39
|
+
}
|
|
40
|
+
static introspectRecursive(obj, depth, options) {
|
|
41
|
+
// Prevent infinite recursion
|
|
42
|
+
if (depth >= options.maxDepth) {
|
|
43
|
+
return { type: 'object', additionalProperties: true };
|
|
44
|
+
}
|
|
45
|
+
// Handle null
|
|
46
|
+
if (obj === null) {
|
|
47
|
+
return { type: 'null' };
|
|
48
|
+
}
|
|
49
|
+
// Handle arrays
|
|
50
|
+
if (Array.isArray(obj)) {
|
|
51
|
+
if (obj.length === 0) {
|
|
52
|
+
return { type: 'array', items: {} };
|
|
53
|
+
}
|
|
54
|
+
// Generate schema from first item
|
|
55
|
+
const itemSchema = this.introspectRecursive(obj[0], depth + 1, options);
|
|
56
|
+
const schema = {
|
|
57
|
+
type: 'array',
|
|
58
|
+
items: itemSchema,
|
|
59
|
+
};
|
|
60
|
+
if (options.includeExamples) {
|
|
61
|
+
schema.examples = [obj];
|
|
62
|
+
}
|
|
63
|
+
return schema;
|
|
64
|
+
}
|
|
65
|
+
const type = typeof obj;
|
|
66
|
+
// Handle primitives
|
|
67
|
+
if (type === 'string') {
|
|
68
|
+
const schema = { type: 'string' };
|
|
69
|
+
if (options.includeExamples) {
|
|
70
|
+
schema.examples = [obj];
|
|
71
|
+
}
|
|
72
|
+
return schema;
|
|
73
|
+
}
|
|
74
|
+
if (type === 'number') {
|
|
75
|
+
const schema = { type: 'number' };
|
|
76
|
+
if (options.includeExamples) {
|
|
77
|
+
schema.examples = [obj];
|
|
78
|
+
}
|
|
79
|
+
return schema;
|
|
80
|
+
}
|
|
81
|
+
if (type === 'boolean') {
|
|
82
|
+
const schema = { type: 'boolean' };
|
|
83
|
+
if (options.includeExamples) {
|
|
84
|
+
schema.examples = [obj];
|
|
85
|
+
}
|
|
86
|
+
return schema;
|
|
87
|
+
}
|
|
88
|
+
// Handle objects
|
|
89
|
+
if (type === 'object') {
|
|
90
|
+
const properties = {};
|
|
91
|
+
const required = [];
|
|
92
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
93
|
+
properties[key] = this.introspectRecursive(value, depth + 1, options);
|
|
94
|
+
// Add to required array based on options
|
|
95
|
+
if (options.strictRequired) {
|
|
96
|
+
required.push(key);
|
|
97
|
+
}
|
|
98
|
+
else if (value !== null && value !== undefined) {
|
|
99
|
+
required.push(key);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const schema = {
|
|
103
|
+
type: 'object',
|
|
104
|
+
properties,
|
|
105
|
+
};
|
|
106
|
+
if (required.length > 0) {
|
|
107
|
+
schema.required = required;
|
|
108
|
+
}
|
|
109
|
+
if (options.includeExamples) {
|
|
110
|
+
schema.examples = [obj];
|
|
111
|
+
}
|
|
112
|
+
return schema;
|
|
113
|
+
}
|
|
114
|
+
// Fallback for unknown types
|
|
115
|
+
return { type: 'string' };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.JsonSchemaIntrospector = JsonSchemaIntrospector;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonSchemaIntrospector.unit.test.d.ts","sourceRoot":"","sources":["../../src/schema/JsonSchemaIntrospector.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const JsonSchemaIntrospector_1 = require("./JsonSchemaIntrospector");
|
|
4
|
+
describe('JsonSchemaIntrospector', () => {
|
|
5
|
+
describe('introspectToJsonSchema', () => {
|
|
6
|
+
it('should handle primitive types', () => {
|
|
7
|
+
expect(JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema('test')).toEqual({
|
|
8
|
+
type: 'string',
|
|
9
|
+
});
|
|
10
|
+
expect(JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(42)).toEqual({
|
|
11
|
+
type: 'number',
|
|
12
|
+
});
|
|
13
|
+
expect(JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(true)).toEqual({
|
|
14
|
+
type: 'boolean',
|
|
15
|
+
});
|
|
16
|
+
expect(JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(null)).toEqual({
|
|
17
|
+
type: 'null',
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
it('should handle simple objects', () => {
|
|
21
|
+
const obj = {
|
|
22
|
+
id: 1,
|
|
23
|
+
name: 'John',
|
|
24
|
+
active: true,
|
|
25
|
+
};
|
|
26
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj);
|
|
27
|
+
expect(schema).toEqual({
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
id: { type: 'number' },
|
|
31
|
+
name: { type: 'string' },
|
|
32
|
+
active: { type: 'boolean' },
|
|
33
|
+
},
|
|
34
|
+
required: ['id', 'name', 'active'],
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
it('should handle nested objects', () => {
|
|
38
|
+
const obj = {
|
|
39
|
+
user: {
|
|
40
|
+
id: 1,
|
|
41
|
+
profile: {
|
|
42
|
+
name: 'John',
|
|
43
|
+
age: 30,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj);
|
|
48
|
+
expect(schema.type).toBe('object');
|
|
49
|
+
expect(schema.properties.user.type).toBe('object');
|
|
50
|
+
expect(schema.properties.user.properties.profile.type).toBe('object');
|
|
51
|
+
expect(schema.properties.user.properties.profile.properties.name).toEqual({
|
|
52
|
+
type: 'string',
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it('should handle arrays', () => {
|
|
56
|
+
const obj = {
|
|
57
|
+
items: [1, 2, 3],
|
|
58
|
+
};
|
|
59
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj);
|
|
60
|
+
expect(schema.properties.items).toEqual({
|
|
61
|
+
type: 'array',
|
|
62
|
+
items: { type: 'number' },
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
it('should handle arrays of objects', () => {
|
|
66
|
+
const obj = {
|
|
67
|
+
users: [
|
|
68
|
+
{ id: 1, name: 'John' },
|
|
69
|
+
{ id: 2, name: 'Jane' },
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj);
|
|
73
|
+
expect(schema.properties.users).toEqual({
|
|
74
|
+
type: 'array',
|
|
75
|
+
items: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
id: { type: 'number' },
|
|
79
|
+
name: { type: 'string' },
|
|
80
|
+
},
|
|
81
|
+
required: ['id', 'name'],
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
it('should handle empty arrays', () => {
|
|
86
|
+
const obj = { items: [] };
|
|
87
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj);
|
|
88
|
+
expect(schema.properties.items).toEqual({
|
|
89
|
+
type: 'array',
|
|
90
|
+
items: {},
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
it('should respect maxDepth option', () => {
|
|
94
|
+
const deepObj = {
|
|
95
|
+
level1: {
|
|
96
|
+
level2: {
|
|
97
|
+
level3: {
|
|
98
|
+
level4: {
|
|
99
|
+
level5: {
|
|
100
|
+
tooDeep: 'value',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(deepObj, {
|
|
108
|
+
maxDepth: 2,
|
|
109
|
+
});
|
|
110
|
+
// Should cut off at depth 2 (level1 -> level2 -> stop)
|
|
111
|
+
expect(schema.properties.level1.properties.level2).toEqual({
|
|
112
|
+
type: 'object',
|
|
113
|
+
additionalProperties: true,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
it('should include examples when requested', () => {
|
|
117
|
+
const obj = { name: 'John' };
|
|
118
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj, {
|
|
119
|
+
includeExamples: true,
|
|
120
|
+
});
|
|
121
|
+
expect(schema.examples).toEqual([{ name: 'John' }]);
|
|
122
|
+
expect(schema.properties.name.examples).toEqual(['John']);
|
|
123
|
+
});
|
|
124
|
+
it('should handle strictRequired option', () => {
|
|
125
|
+
const obj = {
|
|
126
|
+
present: 'value',
|
|
127
|
+
// In real use, there might be optional fields
|
|
128
|
+
};
|
|
129
|
+
const strictSchema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj, {
|
|
130
|
+
strictRequired: true,
|
|
131
|
+
});
|
|
132
|
+
expect(strictSchema.required).toEqual(['present']);
|
|
133
|
+
const relaxedSchema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj, {
|
|
134
|
+
strictRequired: false,
|
|
135
|
+
});
|
|
136
|
+
expect(relaxedSchema.required).toEqual(['present']);
|
|
137
|
+
});
|
|
138
|
+
it('should handle complex real-world example', () => {
|
|
139
|
+
const obj = {
|
|
140
|
+
id: 1,
|
|
141
|
+
title: 'Post Title',
|
|
142
|
+
body: 'Post content',
|
|
143
|
+
userId: 10,
|
|
144
|
+
metadata: {
|
|
145
|
+
created: '2024-01-01',
|
|
146
|
+
tags: ['tech', 'tutorial'],
|
|
147
|
+
stats: {
|
|
148
|
+
views: 100,
|
|
149
|
+
likes: 25,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
comments: [
|
|
153
|
+
{
|
|
154
|
+
id: 1,
|
|
155
|
+
text: 'Great post!',
|
|
156
|
+
author: 'Alice',
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
const schema = JsonSchemaIntrospector_1.JsonSchemaIntrospector.introspectToJsonSchema(obj);
|
|
161
|
+
expect(schema.type).toBe('object');
|
|
162
|
+
expect(schema.properties.id.type).toBe('number');
|
|
163
|
+
expect(schema.properties.metadata.type).toBe('object');
|
|
164
|
+
expect(schema.properties.metadata.properties.tags.type).toBe('array');
|
|
165
|
+
expect(schema.properties.comments.type).toBe('array');
|
|
166
|
+
expect(schema.properties.comments.items.type).toBe('object');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,oBAAoB,GACpB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JsonSchemaIntrospector = void 0;
|
|
4
|
+
var JsonSchemaIntrospector_1 = require("./JsonSchemaIntrospector");
|
|
5
|
+
Object.defineProperty(exports, "JsonSchemaIntrospector", { enumerable: true, get: function () { return JsonSchemaIntrospector_1.JsonSchemaIntrospector; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trayio/commons",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.0-unstable",
|
|
4
4
|
"description": "Extensions to the standard/core libraries and basic features",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./*": "./dist/*.js"
|
|
@@ -45,5 +45,6 @@
|
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"/dist"
|
|
48
|
-
]
|
|
48
|
+
],
|
|
49
|
+
"stableVersion": "0.0.0"
|
|
49
50
|
}
|