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,215 +1,215 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TypeConverter — Schema type conversion utilities (static methods).
|
|
3
|
-
*
|
|
4
|
-
* Maps JSON Schema types to target-format types:
|
|
5
|
-
* - JSON Schema type
|
|
6
|
-
* - MongoDB BSON type
|
|
7
|
-
* - MySQL column type
|
|
8
|
-
* - PostgreSQL column type
|
|
9
|
-
*
|
|
10
|
-
* Also includes format helpers: normalizePropertyName, formatToRegex, mergeSchemas, extractConstraints.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { JSONSchema } from '../types/schema.js'
|
|
14
|
-
|
|
15
|
-
// ==================== Type aliases ====================
|
|
16
|
-
|
|
17
|
-
export type JSType = string | string[]
|
|
18
|
-
|
|
19
|
-
const MYSQL_INTEGER_RANGES = [
|
|
20
|
-
{ type: 'TINYINT', min: -128, max: 127 },
|
|
21
|
-
{ type: 'SMALLINT', min: -32768, max: 32767 },
|
|
22
|
-
{ type: 'INT', min: -2147483648, max: 2147483647 },
|
|
23
|
-
] as const
|
|
24
|
-
|
|
25
|
-
// ==================== TypeConverter ====================
|
|
26
|
-
|
|
27
|
-
export class TypeConverter {
|
|
28
|
-
// ========== JSON Schema types ==========
|
|
29
|
-
|
|
30
|
-
static toJSONSchemaType(nativeType: string): string {
|
|
31
|
-
const mapping: Record<string, string> = {
|
|
32
|
-
string: 'string',
|
|
33
|
-
number: 'number',
|
|
34
|
-
integer: 'integer',
|
|
35
|
-
boolean: 'boolean',
|
|
36
|
-
object: 'object',
|
|
37
|
-
array: 'array',
|
|
38
|
-
null: 'null',
|
|
39
|
-
any: 'string',
|
|
40
|
-
email: 'string',
|
|
41
|
-
url: 'string',
|
|
42
|
-
uuid: 'string',
|
|
43
|
-
date: 'string',
|
|
44
|
-
datetime: 'string',
|
|
45
|
-
}
|
|
46
|
-
return mapping[nativeType.toLowerCase()] ?? 'string'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ========== MongoDB types ==========
|
|
50
|
-
|
|
51
|
-
static toMongoDBType(jsonSchemaType: JSType): string {
|
|
52
|
-
const t = Array.isArray(jsonSchemaType) ? jsonSchemaType[0] : jsonSchemaType
|
|
53
|
-
const mapping: Record<string, string> = {
|
|
54
|
-
string: 'string',
|
|
55
|
-
number: 'double',
|
|
56
|
-
integer: 'int',
|
|
57
|
-
boolean: 'bool',
|
|
58
|
-
object: 'object',
|
|
59
|
-
array: 'array',
|
|
60
|
-
null: 'null',
|
|
61
|
-
}
|
|
62
|
-
return mapping[String(t).toLowerCase()] ?? 'string'
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ========== MySQL types ==========
|
|
66
|
-
|
|
67
|
-
static toMySQLType(jsonSchemaType: JSType, schema?: JSONSchema): string {
|
|
68
|
-
const t = Array.isArray(jsonSchemaType) ? jsonSchemaType[0] : jsonSchemaType
|
|
69
|
-
|
|
70
|
-
// Enum detection: if schema has enum values, use ENUM type
|
|
71
|
-
if (schema?.enum && Array.isArray(schema.enum)) {
|
|
72
|
-
const values = (schema.enum as unknown[]).map(v => `'${String(v).replace(/'/g, "''")}'`).join(', ')
|
|
73
|
-
return `ENUM(${values})`
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
switch (String(t).toLowerCase()) {
|
|
77
|
-
case 'string': {
|
|
78
|
-
// Check for date format
|
|
79
|
-
if (schema?.format === 'date' || schema?.format === 'date-time') {
|
|
80
|
-
return 'DATETIME'
|
|
81
|
-
}
|
|
82
|
-
const maxLen = schema?.maxLength ?? 255
|
|
83
|
-
if (maxLen <= 255) return `VARCHAR(${maxLen})`
|
|
84
|
-
if (maxLen <= 65535) return 'TEXT'
|
|
85
|
-
return 'LONGTEXT'
|
|
86
|
-
}
|
|
87
|
-
case 'number':
|
|
88
|
-
return 'DOUBLE'
|
|
89
|
-
case 'integer': {
|
|
90
|
-
const max = schema?.maximum
|
|
91
|
-
const min = schema?.minimum
|
|
92
|
-
|
|
93
|
-
for (const range of MYSQL_INTEGER_RANGES) {
|
|
94
|
-
if (min !== undefined && max !== undefined && min >= range.min && max <= range.max) {
|
|
95
|
-
return range.type
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return 'BIGINT'
|
|
100
|
-
}
|
|
101
|
-
case 'boolean':
|
|
102
|
-
return 'BOOLEAN'
|
|
103
|
-
case 'object':
|
|
104
|
-
return 'JSON'
|
|
105
|
-
case 'array':
|
|
106
|
-
return 'JSON'
|
|
107
|
-
case 'null':
|
|
108
|
-
return '
|
|
109
|
-
default:
|
|
110
|
-
return 'VARCHAR(255)'
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ========== PostgreSQL types ==========
|
|
115
|
-
|
|
116
|
-
static toPostgreSQLType(jsonSchemaType: JSType, schema?: JSONSchema): string {
|
|
117
|
-
const t = Array.isArray(jsonSchemaType) ? jsonSchemaType[0] : jsonSchemaType
|
|
118
|
-
|
|
119
|
-
switch (String(t).toLowerCase()) {
|
|
120
|
-
case 'string': {
|
|
121
|
-
// Handle date format
|
|
122
|
-
if (schema?.format === 'date') return 'DATE'
|
|
123
|
-
if (schema?.format === 'date-time') return 'TIMESTAMP'
|
|
124
|
-
const maxLen = schema?.maxLength
|
|
125
|
-
if (maxLen !== undefined && maxLen <= 255) return `VARCHAR(${maxLen})`
|
|
126
|
-
if (maxLen !== undefined) return 'TEXT'
|
|
127
|
-
return 'VARCHAR(255)'
|
|
128
|
-
}
|
|
129
|
-
case 'number':
|
|
130
|
-
return 'DOUBLE PRECISION'
|
|
131
|
-
case 'integer':
|
|
132
|
-
return 'BIGINT'
|
|
133
|
-
case 'boolean':
|
|
134
|
-
return 'BOOLEAN'
|
|
135
|
-
case 'object':
|
|
136
|
-
return 'JSONB'
|
|
137
|
-
case 'array':
|
|
138
|
-
return 'JSONB'
|
|
139
|
-
case 'null':
|
|
140
|
-
return '
|
|
141
|
-
default:
|
|
142
|
-
return 'TEXT'
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// ========== Property name normalisation ==========
|
|
147
|
-
|
|
148
|
-
static normalizePropertyName(name: string): string {
|
|
149
|
-
return name
|
|
150
|
-
.trim()
|
|
151
|
-
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
152
|
-
.replace(/^(\d)/, '_$1')
|
|
153
|
-
.replace(/_+/g, '_')
|
|
154
|
-
.replace(/^_+|_+$/g, '')
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ========== Format → RegExp ==========
|
|
158
|
-
|
|
159
|
-
static formatToRegex(format: string): RegExp | null {
|
|
160
|
-
const formats: Record<string, RegExp> = {
|
|
161
|
-
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
162
|
-
uri: /^https?:\/\/.+/,
|
|
163
|
-
date: /^\d{4}-\d{2}-\d{2}$/,
|
|
164
|
-
'date-time': /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/,
|
|
165
|
-
time: /^\d{2}:\d{2}:\d{2}$/,
|
|
166
|
-
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
167
|
-
ipv4: /^(\d{1,3}\.){3}\d{1,3}$/,
|
|
168
|
-
ipv6: /^[0-9a-f:]+$/i,
|
|
169
|
-
}
|
|
170
|
-
return formats[format] ?? null
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ========== Schema merging ==========
|
|
174
|
-
|
|
175
|
-
static mergeSchemas(...schemas: JSONSchema[]): JSONSchema {
|
|
176
|
-
const result: Record<string, unknown> = {}
|
|
177
|
-
|
|
178
|
-
for (const schema of schemas) {
|
|
179
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
180
|
-
if (key === 'properties') {
|
|
181
|
-
result['properties'] = {
|
|
182
|
-
...((result['properties'] as Record<string, unknown>) ?? {}),
|
|
183
|
-
...(value as Record<string, unknown>),
|
|
184
|
-
}
|
|
185
|
-
} else if (key === 'required') {
|
|
186
|
-
const existing = (result['required'] as string[]) ?? []
|
|
187
|
-
result['required'] = [...new Set([...existing, ...(value as string[])])]
|
|
188
|
-
} else {
|
|
189
|
-
result[key] = value
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return result as JSONSchema
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// ========== Constraint extraction ==========
|
|
198
|
-
|
|
199
|
-
static extractConstraints(schema: JSONSchema): Record<string, unknown> {
|
|
200
|
-
const constraintKeys = [
|
|
201
|
-
'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum',
|
|
202
|
-
'multipleOf', 'minLength', 'maxLength', 'pattern',
|
|
203
|
-
'minItems', 'maxItems', 'uniqueItems', 'minProperties', 'maxProperties',
|
|
204
|
-
'enum', 'format', 'const',
|
|
205
|
-
] as const
|
|
206
|
-
|
|
207
|
-
const result: Record<string, unknown> = {}
|
|
208
|
-
for (const key of constraintKeys) {
|
|
209
|
-
if (key in schema) {
|
|
210
|
-
result[key] = (schema as Record<string, unknown>)[key]
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return result
|
|
214
|
-
}
|
|
215
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* TypeConverter — Schema type conversion utilities (static methods).
|
|
3
|
+
*
|
|
4
|
+
* Maps JSON Schema types to target-format types:
|
|
5
|
+
* - JSON Schema type
|
|
6
|
+
* - MongoDB BSON type
|
|
7
|
+
* - MySQL column type
|
|
8
|
+
* - PostgreSQL column type
|
|
9
|
+
*
|
|
10
|
+
* Also includes format helpers: normalizePropertyName, formatToRegex, mergeSchemas, extractConstraints.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { JSONSchema } from '../types/schema.js'
|
|
14
|
+
|
|
15
|
+
// ==================== Type aliases ====================
|
|
16
|
+
|
|
17
|
+
export type JSType = string | string[]
|
|
18
|
+
|
|
19
|
+
const MYSQL_INTEGER_RANGES = [
|
|
20
|
+
{ type: 'TINYINT', min: -128, max: 127 },
|
|
21
|
+
{ type: 'SMALLINT', min: -32768, max: 32767 },
|
|
22
|
+
{ type: 'INT', min: -2147483648, max: 2147483647 },
|
|
23
|
+
] as const
|
|
24
|
+
|
|
25
|
+
// ==================== TypeConverter ====================
|
|
26
|
+
|
|
27
|
+
export class TypeConverter {
|
|
28
|
+
// ========== JSON Schema types ==========
|
|
29
|
+
|
|
30
|
+
static toJSONSchemaType(nativeType: string): string {
|
|
31
|
+
const mapping: Record<string, string> = {
|
|
32
|
+
string: 'string',
|
|
33
|
+
number: 'number',
|
|
34
|
+
integer: 'integer',
|
|
35
|
+
boolean: 'boolean',
|
|
36
|
+
object: 'object',
|
|
37
|
+
array: 'array',
|
|
38
|
+
null: 'null',
|
|
39
|
+
any: 'string',
|
|
40
|
+
email: 'string',
|
|
41
|
+
url: 'string',
|
|
42
|
+
uuid: 'string',
|
|
43
|
+
date: 'string',
|
|
44
|
+
datetime: 'string',
|
|
45
|
+
}
|
|
46
|
+
return mapping[nativeType.toLowerCase()] ?? 'string'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ========== MongoDB types ==========
|
|
50
|
+
|
|
51
|
+
static toMongoDBType(jsonSchemaType: JSType): string {
|
|
52
|
+
const t = Array.isArray(jsonSchemaType) ? jsonSchemaType[0] : jsonSchemaType
|
|
53
|
+
const mapping: Record<string, string> = {
|
|
54
|
+
string: 'string',
|
|
55
|
+
number: 'double',
|
|
56
|
+
integer: 'int',
|
|
57
|
+
boolean: 'bool',
|
|
58
|
+
object: 'object',
|
|
59
|
+
array: 'array',
|
|
60
|
+
null: 'null',
|
|
61
|
+
}
|
|
62
|
+
return mapping[String(t).toLowerCase()] ?? 'string'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ========== MySQL types ==========
|
|
66
|
+
|
|
67
|
+
static toMySQLType(jsonSchemaType: JSType, schema?: JSONSchema): string {
|
|
68
|
+
const t = Array.isArray(jsonSchemaType) ? jsonSchemaType[0] : jsonSchemaType
|
|
69
|
+
|
|
70
|
+
// Enum detection: if schema has enum values, use ENUM type
|
|
71
|
+
if (schema?.enum && Array.isArray(schema.enum)) {
|
|
72
|
+
const values = (schema.enum as unknown[]).map(v => `'${String(v).replace(/'/g, "''")}'`).join(', ')
|
|
73
|
+
return `ENUM(${values})`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
switch (String(t).toLowerCase()) {
|
|
77
|
+
case 'string': {
|
|
78
|
+
// Check for date format
|
|
79
|
+
if (schema?.format === 'date' || schema?.format === 'date-time') {
|
|
80
|
+
return 'DATETIME'
|
|
81
|
+
}
|
|
82
|
+
const maxLen = schema?.maxLength ?? 255
|
|
83
|
+
if (maxLen <= 255) return `VARCHAR(${maxLen})`
|
|
84
|
+
if (maxLen <= 65535) return 'TEXT'
|
|
85
|
+
return 'LONGTEXT'
|
|
86
|
+
}
|
|
87
|
+
case 'number':
|
|
88
|
+
return 'DOUBLE'
|
|
89
|
+
case 'integer': {
|
|
90
|
+
const max = schema?.maximum
|
|
91
|
+
const min = schema?.minimum
|
|
92
|
+
|
|
93
|
+
for (const range of MYSQL_INTEGER_RANGES) {
|
|
94
|
+
if (min !== undefined && max !== undefined && min >= range.min && max <= range.max) {
|
|
95
|
+
return range.type
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return 'BIGINT'
|
|
100
|
+
}
|
|
101
|
+
case 'boolean':
|
|
102
|
+
return 'BOOLEAN'
|
|
103
|
+
case 'object':
|
|
104
|
+
return 'JSON'
|
|
105
|
+
case 'array':
|
|
106
|
+
return 'JSON'
|
|
107
|
+
case 'null':
|
|
108
|
+
return 'TEXT'
|
|
109
|
+
default:
|
|
110
|
+
return 'VARCHAR(255)'
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ========== PostgreSQL types ==========
|
|
115
|
+
|
|
116
|
+
static toPostgreSQLType(jsonSchemaType: JSType, schema?: JSONSchema): string {
|
|
117
|
+
const t = Array.isArray(jsonSchemaType) ? jsonSchemaType[0] : jsonSchemaType
|
|
118
|
+
|
|
119
|
+
switch (String(t).toLowerCase()) {
|
|
120
|
+
case 'string': {
|
|
121
|
+
// Handle date format
|
|
122
|
+
if (schema?.format === 'date') return 'DATE'
|
|
123
|
+
if (schema?.format === 'date-time') return 'TIMESTAMP'
|
|
124
|
+
const maxLen = schema?.maxLength
|
|
125
|
+
if (maxLen !== undefined && maxLen <= 255) return `VARCHAR(${maxLen})`
|
|
126
|
+
if (maxLen !== undefined) return 'TEXT'
|
|
127
|
+
return 'VARCHAR(255)'
|
|
128
|
+
}
|
|
129
|
+
case 'number':
|
|
130
|
+
return 'DOUBLE PRECISION'
|
|
131
|
+
case 'integer':
|
|
132
|
+
return 'BIGINT'
|
|
133
|
+
case 'boolean':
|
|
134
|
+
return 'BOOLEAN'
|
|
135
|
+
case 'object':
|
|
136
|
+
return 'JSONB'
|
|
137
|
+
case 'array':
|
|
138
|
+
return 'JSONB'
|
|
139
|
+
case 'null':
|
|
140
|
+
return 'TEXT'
|
|
141
|
+
default:
|
|
142
|
+
return 'TEXT'
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ========== Property name normalisation ==========
|
|
147
|
+
|
|
148
|
+
static normalizePropertyName(name: string): string {
|
|
149
|
+
return name
|
|
150
|
+
.trim()
|
|
151
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
152
|
+
.replace(/^(\d)/, '_$1')
|
|
153
|
+
.replace(/_+/g, '_')
|
|
154
|
+
.replace(/^_+|_+$/g, '')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ========== Format → RegExp ==========
|
|
158
|
+
|
|
159
|
+
static formatToRegex(format: string): RegExp | null {
|
|
160
|
+
const formats: Record<string, RegExp> = {
|
|
161
|
+
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
162
|
+
uri: /^https?:\/\/.+/,
|
|
163
|
+
date: /^\d{4}-\d{2}-\d{2}$/,
|
|
164
|
+
'date-time': /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/,
|
|
165
|
+
time: /^\d{2}:\d{2}:\d{2}$/,
|
|
166
|
+
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
167
|
+
ipv4: /^(\d{1,3}\.){3}\d{1,3}$/,
|
|
168
|
+
ipv6: /^[0-9a-f:]+$/i,
|
|
169
|
+
}
|
|
170
|
+
return formats[format] ?? null
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ========== Schema merging ==========
|
|
174
|
+
|
|
175
|
+
static mergeSchemas(...schemas: JSONSchema[]): JSONSchema {
|
|
176
|
+
const result: Record<string, unknown> = {}
|
|
177
|
+
|
|
178
|
+
for (const schema of schemas) {
|
|
179
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
180
|
+
if (key === 'properties') {
|
|
181
|
+
result['properties'] = {
|
|
182
|
+
...((result['properties'] as Record<string, unknown>) ?? {}),
|
|
183
|
+
...(value as Record<string, unknown>),
|
|
184
|
+
}
|
|
185
|
+
} else if (key === 'required') {
|
|
186
|
+
const existing = (result['required'] as string[]) ?? []
|
|
187
|
+
result['required'] = [...new Set([...existing, ...(value as string[])])]
|
|
188
|
+
} else {
|
|
189
|
+
result[key] = value
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return result as JSONSchema
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ========== Constraint extraction ==========
|
|
198
|
+
|
|
199
|
+
static extractConstraints(schema: JSONSchema): Record<string, unknown> {
|
|
200
|
+
const constraintKeys = [
|
|
201
|
+
'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum',
|
|
202
|
+
'multipleOf', 'minLength', 'maxLength', 'pattern',
|
|
203
|
+
'minItems', 'maxItems', 'uniqueItems', 'minProperties', 'maxProperties',
|
|
204
|
+
'enum', 'format', 'const',
|
|
205
|
+
] as const
|
|
206
|
+
|
|
207
|
+
const result: Record<string, unknown> = {}
|
|
208
|
+
for (const key of constraintKeys) {
|
|
209
|
+
if (key in schema) {
|
|
210
|
+
result[key] = (schema as Record<string, unknown>)[key]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return result
|
|
214
|
+
}
|
|
215
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* src/utils/index.ts — Utility library unified exports.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { TypeConverter } from './TypeConverter.js'
|
|
6
|
-
export type { JSType } from './TypeConverter.js'
|
|
7
|
-
|
|
8
|
-
export { SchemaHelper } from './SchemaHelper.js'
|
|
9
|
-
|
|
10
|
-
export { SchemaUtils } from './SchemaUtils.js'
|
|
1
|
+
/**
|
|
2
|
+
* src/utils/index.ts — Utility library unified exports.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { TypeConverter } from './TypeConverter.js'
|
|
6
|
+
export type { JSType } from './TypeConverter.js'
|
|
7
|
+
|
|
8
|
+
export { SchemaHelper } from './SchemaHelper.js'
|
|
9
|
+
|
|
10
|
+
export { SchemaUtils } from './SchemaUtils.js'
|