schema-dsl 2.0.0 → 2.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/CHANGELOG.md +130 -113
- package/LICENSE +21 -21
- package/README.md +628 -628
- package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
- package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
- package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
- package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
- package/dist/index.cjs +75 -29
- package/dist/index.d.cts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +75 -29
- package/dist/plugins/custom-format.cjs +33 -17
- package/dist/plugins/custom-format.d.cts +1 -1
- package/dist/plugins/custom-format.d.ts +1 -1
- package/dist/plugins/custom-format.js +33 -17
- package/dist/plugins/custom-type-example.cjs +33 -17
- package/dist/plugins/custom-type-example.d.cts +1 -1
- package/dist/plugins/custom-type-example.d.ts +1 -1
- package/dist/plugins/custom-type-example.js +33 -17
- package/dist/plugins/custom-validator.cjs +0 -2
- package/dist/plugins/custom-validator.d.cts +1 -1
- package/dist/plugins/custom-validator.d.ts +1 -1
- package/dist/plugins/custom-validator.js +0 -2
- package/docs/FEATURE-INDEX.md +553 -553
- package/docs/add-custom-locale.md +496 -496
- package/docs/add-keyword.md +24 -24
- package/docs/api-reference.md +1047 -1047
- package/docs/api.md +13 -13
- package/docs/best-practices-project-structure.md +417 -417
- package/docs/best-practices.md +712 -712
- package/docs/cache-manager.md +344 -344
- package/docs/compile.md +45 -45
- package/docs/conditional-api.md +1307 -1307
- package/docs/custom-extensions-guide.md +339 -339
- package/docs/design-philosophy.md +606 -606
- package/docs/doc-index.md +324 -324
- package/docs/dsl-syntax.md +714 -714
- package/docs/dynamic-locale.md +608 -608
- package/docs/enum.md +482 -482
- package/docs/error-handling.md +1975 -1975
- package/docs/export-guide.md +501 -501
- package/docs/export-limitations.md +567 -567
- package/docs/faq.md +596 -596
- package/docs/frontend-i18n-guide.md +307 -307
- package/docs/i18n-user-guide.md +487 -487
- package/docs/i18n.md +476 -476
- package/docs/index.md +48 -48
- package/docs/json-schema-basics.md +40 -40
- package/docs/label-vs-description.md +271 -271
- package/docs/markdown-exporter.md +406 -406
- package/docs/mongodb-exporter.md +302 -302
- package/docs/multi-language.md +26 -26
- package/docs/multi-type-support.md +322 -322
- package/docs/mysql-exporter.md +280 -280
- package/docs/number-operators.md +449 -449
- package/docs/optional-marker-guide.md +326 -326
- package/docs/performance-guide.md +49 -49
- package/docs/plugin-system.md +381 -381
- package/docs/plugin-type-registration.md +34 -34
- package/docs/postgresql-exporter.md +311 -311
- package/docs/public/favicon.svg +4 -4
- package/docs/quick-start.md +435 -435
- package/docs/runtime-locale-support.md +532 -532
- package/docs/schema-helper.md +345 -345
- package/docs/schema-utils-advanced-issues.md +23 -23
- package/docs/schema-utils-best-practices.md +20 -20
- package/docs/schema-utils-chaining.md +150 -150
- package/docs/schema-utils.md +524 -524
- package/docs/security-checklist.md +20 -20
- package/docs/string-extensions.md +488 -488
- package/docs/troubleshooting.md +486 -486
- package/docs/type-converter.md +310 -310
- package/docs/type-reference.md +242 -242
- package/docs/typescript-guide.md +584 -584
- package/docs/union-type-guide.md +157 -157
- package/docs/union-types.md +284 -284
- package/docs/validate-async.md +491 -491
- package/docs/validate-batch.md +49 -49
- package/docs/validate-dsl-object-support.md +578 -578
- package/docs/validate.md +506 -506
- package/docs/validation-guide.md +502 -502
- package/docs/validator.md +39 -39
- package/package.json +131 -131
- package/plugins/custom-format.cjs +8 -8
- package/plugins/custom-type-example.cjs +8 -8
- package/plugins/custom-validator.cjs +8 -8
- package/src/adapters/DslAdapter.ts +111 -111
- package/src/adapters/index.ts +1 -1
- package/src/config/constants.ts +83 -83
- package/src/config/index.ts +2 -2
- package/src/config/patterns.ts +77 -77
- package/src/core/CacheManager.ts +169 -159
- package/src/core/ConditionalBuilder.ts +382 -382
- package/src/core/ConditionalRuntime.ts +27 -27
- package/src/core/ConditionalValidator.ts +254 -254
- package/src/core/DslBuilder.ts +687 -677
- package/src/core/ErrorCodes.ts +38 -38
- package/src/core/ErrorFormatter.ts +271 -271
- package/src/core/JSONSchemaCore.ts +65 -65
- package/src/core/Locale.ts +187 -187
- package/src/core/MessageTemplate.ts +42 -42
- package/src/core/ObjectDslBuilder.ts +64 -64
- package/src/core/PluginManager.ts +326 -326
- package/src/core/StringExtensions.ts +140 -140
- package/src/core/TemplateEngine.ts +44 -44
- package/src/core/Validator.ts +448 -448
- package/src/errors/I18nError.ts +159 -159
- package/src/errors/ValidationError.ts +105 -105
- package/src/exporters/BaseExporter.ts +60 -60
- package/src/exporters/MarkdownExporter.ts +305 -305
- package/src/exporters/MongoDBExporter.ts +126 -126
- package/src/exporters/MySQLExporter.ts +156 -155
- package/src/exporters/PostgreSQLExporter.ts +222 -222
- package/src/exporters/index.ts +18 -18
- package/src/index.ts +651 -633
- package/src/locales/en-US.ts +160 -160
- package/src/locales/es-ES.ts +160 -160
- package/src/locales/fr-FR.ts +160 -160
- package/src/locales/index.ts +103 -103
- package/src/locales/ja-JP.ts +160 -160
- package/src/locales/types.ts +156 -156
- package/src/locales/zh-CN.ts +160 -160
- package/src/parser/ConstraintParser.ts +101 -101
- package/src/parser/DslParser.ts +470 -470
- package/src/parser/SchemaCompiler.ts +66 -66
- package/src/parser/TypeRegistry.ts +250 -250
- package/src/parser/index.ts +6 -6
- package/src/plugins/custom-format.ts +124 -126
- package/src/plugins/custom-type-example.ts +106 -108
- package/src/plugins/custom-validator.ts +138 -140
- package/src/types/conditional.ts +28 -28
- package/src/types/config.ts +59 -59
- package/src/types/dsl.ts +131 -131
- package/src/types/error.ts +60 -60
- package/src/types/index.ts +17 -17
- package/src/types/infer.ts +127 -127
- package/src/types/plugin.ts +58 -58
- package/src/types/safe-regex.d.ts +9 -9
- package/src/types/schema.ts +66 -66
- package/src/types/validate.ts +71 -71
- package/src/utils/SchemaHelper.ts +196 -196
- package/src/utils/SchemaUtils.ts +365 -346
- package/src/utils/TypeConverter.ts +215 -215
- package/src/utils/index.ts +10 -10
- package/src/validators/CustomKeywords.ts +477 -477
|
@@ -1,196 +1,196 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SchemaHelper — Schema utility functions.
|
|
3
|
-
*
|
|
4
|
-
* Common helpers for JSON Schema structure manipulation:
|
|
5
|
-
* isValidSchema, generateSchemaId, cloneSchema, flattenSchema,
|
|
6
|
-
* getFieldPaths, extractRequiredFields, compareSchemas, simplifySchema,
|
|
7
|
-
* isValidPropertyName, getSchemaComplexity, summarizeSchema
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { JSONSchema } from '../types/schema.js'
|
|
11
|
-
|
|
12
|
-
export class SchemaHelper {
|
|
13
|
-
/**
|
|
14
|
-
* Check whether the value is a valid JSON Schema (must contain at least one of: type / properties / items / $ref).
|
|
15
|
-
*/
|
|
16
|
-
static isValidSchema(schema: unknown): schema is JSONSchema {
|
|
17
|
-
if (!schema || typeof schema !== 'object') return false
|
|
18
|
-
const s = schema as Record<string, unknown>
|
|
19
|
-
return !!(s['type'] || s['properties'] || s['items'] || s['$ref']
|
|
20
|
-
|| s['anyOf'] || s['oneOf'] || s['allOf'] || s['enum'])
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Generate a content-hash-based unique ID for a schema.
|
|
25
|
-
*/
|
|
26
|
-
static generateSchemaId(schema: JSONSchema): string {
|
|
27
|
-
const str = JSON.stringify(schema)
|
|
28
|
-
let hash = 0xcbf29ce484222325n
|
|
29
|
-
const prime = 0x100000001b3n
|
|
30
|
-
for (let i = 0; i < str.length; i++) {
|
|
31
|
-
hash ^= BigInt(str.charCodeAt(i))
|
|
32
|
-
hash = BigInt.asUintN(64, hash * prime)
|
|
33
|
-
}
|
|
34
|
-
return `schema_${hash.toString(36)}`
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Deep-clone a schema via JSON serialisation (Function/RegExp fields are not preserved).
|
|
39
|
-
*/
|
|
40
|
-
static cloneSchema(schema: JSONSchema): JSONSchema {
|
|
41
|
-
return JSON.parse(JSON.stringify(schema)) as JSONSchema
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Flatten a nested schema into dot-separated path form.
|
|
46
|
-
* @param prefix - Property path prefix.
|
|
47
|
-
*/
|
|
48
|
-
static flattenSchema(schema: JSONSchema, prefix = ''): Record<string, JSONSchema> {
|
|
49
|
-
const result: Record<string, JSONSchema> = {}
|
|
50
|
-
|
|
51
|
-
if (schema.properties) {
|
|
52
|
-
for (const [key, value] of Object.entries(schema.properties)) {
|
|
53
|
-
const fullKey = prefix ? `${prefix}.${key}` : key
|
|
54
|
-
if (value.type === 'object' && value.properties) {
|
|
55
|
-
Object.assign(result, this.flattenSchema(value, fullKey))
|
|
56
|
-
} else {
|
|
57
|
-
result[fullKey] = value
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return result
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Get all field paths in a schema (including nested object and array paths).
|
|
67
|
-
*/
|
|
68
|
-
static getFieldPaths(schema: JSONSchema): string[] {
|
|
69
|
-
const paths: string[] = []
|
|
70
|
-
|
|
71
|
-
function traverse(obj: JSONSchema, currentPath = ''): void {
|
|
72
|
-
if (obj.properties) {
|
|
73
|
-
for (const [key, value] of Object.entries(obj.properties)) {
|
|
74
|
-
const path = currentPath ? `${currentPath}.${key}` : key
|
|
75
|
-
paths.push(path)
|
|
76
|
-
if (value.type === 'object') {
|
|
77
|
-
traverse(value, path)
|
|
78
|
-
} else if (value.type === 'array' && value.items) {
|
|
79
|
-
traverse(value.items as JSONSchema, `${path}[]`)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
traverse(schema)
|
|
86
|
-
return paths
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Extract all required fields from a schema (including nested paths).
|
|
91
|
-
*/
|
|
92
|
-
static extractRequiredFields(schema: JSONSchema): string[] {
|
|
93
|
-
const required: string[] = []
|
|
94
|
-
|
|
95
|
-
function traverse(obj: JSONSchema, prefix = ''): void {
|
|
96
|
-
if (obj.required && Array.isArray(obj.required)) {
|
|
97
|
-
for (const field of obj.required) {
|
|
98
|
-
required.push(prefix ? `${prefix}.${field}` : field)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (obj.properties) {
|
|
102
|
-
for (const [key, value] of Object.entries(obj.properties)) {
|
|
103
|
-
if (value.type === 'object') {
|
|
104
|
-
traverse(value, prefix ? `${prefix}.${key}` : key)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
traverse(schema)
|
|
111
|
-
return required
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Shallow-compare two schemas for equality (via JSON serialisation).
|
|
116
|
-
*/
|
|
117
|
-
static compareSchemas(schema1: JSONSchema, schema2: JSONSchema): boolean {
|
|
118
|
-
return JSON.stringify(schema1) === JSON.stringify(schema2)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Simplify a schema by removing $schema, empty properties, and empty required arrays.
|
|
123
|
-
*/
|
|
124
|
-
static simplifySchema(schema: JSONSchema): JSONSchema {
|
|
125
|
-
const simplified = this.cloneSchema(schema) as Record<string, unknown>
|
|
126
|
-
|
|
127
|
-
delete simplified['$schema']
|
|
128
|
-
|
|
129
|
-
const props = simplified['properties'] as Record<string, unknown> | undefined
|
|
130
|
-
if (props && Object.keys(props).length === 0) {
|
|
131
|
-
delete simplified['properties']
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const req = simplified['required'] as unknown[] | undefined
|
|
135
|
-
if (req && req.length === 0) {
|
|
136
|
-
delete simplified['required']
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return simplified as JSONSchema
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Validate that a property name is legal (letters/digits/underscores/hyphens; must start with a letter or underscore).
|
|
144
|
-
*/
|
|
145
|
-
static isValidPropertyName(name: string): boolean {
|
|
146
|
-
return /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Get the maximum nesting depth (complexity) of a schema.
|
|
151
|
-
*/
|
|
152
|
-
static getSchemaComplexity(schema: JSONSchema): number {
|
|
153
|
-
let maxDepth = 0
|
|
154
|
-
|
|
155
|
-
function traverse(obj: JSONSchema, depth: number): void {
|
|
156
|
-
maxDepth = Math.max(maxDepth, depth)
|
|
157
|
-
if (obj.properties) {
|
|
158
|
-
for (const value of Object.values(obj.properties)) {
|
|
159
|
-
if (value.type === 'object') {
|
|
160
|
-
traverse(value, depth + 1)
|
|
161
|
-
} else if (value.type === 'array' && value.items) {
|
|
162
|
-
traverse(value.items as JSONSchema, depth + 1)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
traverse(schema, 0)
|
|
169
|
-
return maxDepth
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Generate a summary of schema metadata.
|
|
174
|
-
*/
|
|
175
|
-
static summarizeSchema(schema: JSONSchema): {
|
|
176
|
-
type: string
|
|
177
|
-
fieldCount: number
|
|
178
|
-
requiredCount: number
|
|
179
|
-
complexity: number
|
|
180
|
-
hasNested: boolean
|
|
181
|
-
fields: string[]
|
|
182
|
-
} {
|
|
183
|
-
const fields = this.getFieldPaths(schema)
|
|
184
|
-
const requiredFields = this.extractRequiredFields(schema)
|
|
185
|
-
const complexity = this.getSchemaComplexity(schema)
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
type: (schema.type as string) ?? 'unknown',
|
|
189
|
-
fieldCount: fields.length,
|
|
190
|
-
requiredCount: requiredFields.length,
|
|
191
|
-
complexity,
|
|
192
|
-
hasNested: complexity > 0,
|
|
193
|
-
fields,
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* SchemaHelper — Schema utility functions.
|
|
3
|
+
*
|
|
4
|
+
* Common helpers for JSON Schema structure manipulation:
|
|
5
|
+
* isValidSchema, generateSchemaId, cloneSchema, flattenSchema,
|
|
6
|
+
* getFieldPaths, extractRequiredFields, compareSchemas, simplifySchema,
|
|
7
|
+
* isValidPropertyName, getSchemaComplexity, summarizeSchema
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { JSONSchema } from '../types/schema.js'
|
|
11
|
+
|
|
12
|
+
export class SchemaHelper {
|
|
13
|
+
/**
|
|
14
|
+
* Check whether the value is a valid JSON Schema (must contain at least one of: type / properties / items / $ref).
|
|
15
|
+
*/
|
|
16
|
+
static isValidSchema(schema: unknown): schema is JSONSchema {
|
|
17
|
+
if (!schema || typeof schema !== 'object') return false
|
|
18
|
+
const s = schema as Record<string, unknown>
|
|
19
|
+
return !!(s['type'] || s['properties'] || s['items'] || s['$ref']
|
|
20
|
+
|| s['anyOf'] || s['oneOf'] || s['allOf'] || s['enum'])
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate a content-hash-based unique ID for a schema.
|
|
25
|
+
*/
|
|
26
|
+
static generateSchemaId(schema: JSONSchema): string {
|
|
27
|
+
const str = JSON.stringify(schema)
|
|
28
|
+
let hash = 0xcbf29ce484222325n
|
|
29
|
+
const prime = 0x100000001b3n
|
|
30
|
+
for (let i = 0; i < str.length; i++) {
|
|
31
|
+
hash ^= BigInt(str.charCodeAt(i))
|
|
32
|
+
hash = BigInt.asUintN(64, hash * prime)
|
|
33
|
+
}
|
|
34
|
+
return `schema_${hash.toString(36)}`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Deep-clone a schema via JSON serialisation (Function/RegExp fields are not preserved).
|
|
39
|
+
*/
|
|
40
|
+
static cloneSchema(schema: JSONSchema): JSONSchema {
|
|
41
|
+
return JSON.parse(JSON.stringify(schema)) as JSONSchema
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Flatten a nested schema into dot-separated path form.
|
|
46
|
+
* @param prefix - Property path prefix.
|
|
47
|
+
*/
|
|
48
|
+
static flattenSchema(schema: JSONSchema, prefix = ''): Record<string, JSONSchema> {
|
|
49
|
+
const result: Record<string, JSONSchema> = {}
|
|
50
|
+
|
|
51
|
+
if (schema.properties) {
|
|
52
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
53
|
+
const fullKey = prefix ? `${prefix}.${key}` : key
|
|
54
|
+
if (value.type === 'object' && value.properties) {
|
|
55
|
+
Object.assign(result, this.flattenSchema(value, fullKey))
|
|
56
|
+
} else {
|
|
57
|
+
result[fullKey] = value
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get all field paths in a schema (including nested object and array paths).
|
|
67
|
+
*/
|
|
68
|
+
static getFieldPaths(schema: JSONSchema): string[] {
|
|
69
|
+
const paths: string[] = []
|
|
70
|
+
|
|
71
|
+
function traverse(obj: JSONSchema, currentPath = ''): void {
|
|
72
|
+
if (obj.properties) {
|
|
73
|
+
for (const [key, value] of Object.entries(obj.properties)) {
|
|
74
|
+
const path = currentPath ? `${currentPath}.${key}` : key
|
|
75
|
+
paths.push(path)
|
|
76
|
+
if (value.type === 'object') {
|
|
77
|
+
traverse(value, path)
|
|
78
|
+
} else if (value.type === 'array' && value.items) {
|
|
79
|
+
traverse(value.items as JSONSchema, `${path}[]`)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
traverse(schema)
|
|
86
|
+
return paths
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Extract all required fields from a schema (including nested paths).
|
|
91
|
+
*/
|
|
92
|
+
static extractRequiredFields(schema: JSONSchema): string[] {
|
|
93
|
+
const required: string[] = []
|
|
94
|
+
|
|
95
|
+
function traverse(obj: JSONSchema, prefix = ''): void {
|
|
96
|
+
if (obj.required && Array.isArray(obj.required)) {
|
|
97
|
+
for (const field of obj.required) {
|
|
98
|
+
required.push(prefix ? `${prefix}.${field}` : field)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (obj.properties) {
|
|
102
|
+
for (const [key, value] of Object.entries(obj.properties)) {
|
|
103
|
+
if (value.type === 'object') {
|
|
104
|
+
traverse(value, prefix ? `${prefix}.${key}` : key)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
traverse(schema)
|
|
111
|
+
return required
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Shallow-compare two schemas for equality (via JSON serialisation).
|
|
116
|
+
*/
|
|
117
|
+
static compareSchemas(schema1: JSONSchema, schema2: JSONSchema): boolean {
|
|
118
|
+
return JSON.stringify(schema1) === JSON.stringify(schema2)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Simplify a schema by removing $schema, empty properties, and empty required arrays.
|
|
123
|
+
*/
|
|
124
|
+
static simplifySchema(schema: JSONSchema): JSONSchema {
|
|
125
|
+
const simplified = this.cloneSchema(schema) as Record<string, unknown>
|
|
126
|
+
|
|
127
|
+
delete simplified['$schema']
|
|
128
|
+
|
|
129
|
+
const props = simplified['properties'] as Record<string, unknown> | undefined
|
|
130
|
+
if (props && Object.keys(props).length === 0) {
|
|
131
|
+
delete simplified['properties']
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const req = simplified['required'] as unknown[] | undefined
|
|
135
|
+
if (req && req.length === 0) {
|
|
136
|
+
delete simplified['required']
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return simplified as JSONSchema
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Validate that a property name is legal (letters/digits/underscores/hyphens; must start with a letter or underscore).
|
|
144
|
+
*/
|
|
145
|
+
static isValidPropertyName(name: string): boolean {
|
|
146
|
+
return /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get the maximum nesting depth (complexity) of a schema.
|
|
151
|
+
*/
|
|
152
|
+
static getSchemaComplexity(schema: JSONSchema): number {
|
|
153
|
+
let maxDepth = 0
|
|
154
|
+
|
|
155
|
+
function traverse(obj: JSONSchema, depth: number): void {
|
|
156
|
+
maxDepth = Math.max(maxDepth, depth)
|
|
157
|
+
if (obj.properties) {
|
|
158
|
+
for (const value of Object.values(obj.properties)) {
|
|
159
|
+
if (value.type === 'object') {
|
|
160
|
+
traverse(value, depth + 1)
|
|
161
|
+
} else if (value.type === 'array' && value.items) {
|
|
162
|
+
traverse(value.items as JSONSchema, depth + 1)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
traverse(schema, 0)
|
|
169
|
+
return maxDepth
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Generate a summary of schema metadata.
|
|
174
|
+
*/
|
|
175
|
+
static summarizeSchema(schema: JSONSchema): {
|
|
176
|
+
type: string
|
|
177
|
+
fieldCount: number
|
|
178
|
+
requiredCount: number
|
|
179
|
+
complexity: number
|
|
180
|
+
hasNested: boolean
|
|
181
|
+
fields: string[]
|
|
182
|
+
} {
|
|
183
|
+
const fields = this.getFieldPaths(schema)
|
|
184
|
+
const requiredFields = this.extractRequiredFields(schema)
|
|
185
|
+
const complexity = this.getSchemaComplexity(schema)
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
type: (schema.type as string) ?? 'unknown',
|
|
189
|
+
fieldCount: fields.length,
|
|
190
|
+
requiredCount: requiredFields.length,
|
|
191
|
+
complexity,
|
|
192
|
+
hasNested: complexity > 0,
|
|
193
|
+
fields,
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|