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
package/README.md
CHANGED
|
@@ -1,628 +1,628 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
# 🎯 schema-dsl
|
|
4
|
-
|
|
5
|
-
**Declare field rules with the simplest DSL — let one schema drive validation, derivation, export, and documentation.**
|
|
6
|
-
|
|
7
|
-
[](https://www.npmjs.com/package/schema-dsl)
|
|
8
|
-
[](https://www.npmjs.com/package/schema-dsl)
|
|
9
|
-
[](https://github.com/vextjs/schema-dsl/actions)
|
|
10
|
-
[](https://www.typescriptlang.org/)
|
|
11
|
-
[](https://opensource.org/licenses/MIT)
|
|
12
|
-
|
|
13
|
-
[Quick Start](#-quick-start) · [Documentation](https://vextjs.github.io/schema-dsl) · [Feature Overview](#-feature-overview) · [Examples](./examples)
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install schema-dsl
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## ⚡ TL;DR (30-second intro)
|
|
24
|
-
|
|
25
|
-
**What is schema-dsl?**
|
|
26
|
-
|
|
27
|
-
Write field rules like this:
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
import { dsl, validate } from 'schema-dsl';
|
|
31
|
-
|
|
32
|
-
const userSchema = dsl({
|
|
33
|
-
username: 'string:3-32!',
|
|
34
|
-
email: 'email!',
|
|
35
|
-
role: 'admin|user|guest',
|
|
36
|
-
contact: 'types:email|phone'
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const result = validate(userSchema, req.body);
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Then that **same set of rules** continues to power:
|
|
43
|
-
|
|
44
|
-
- ✅ **Sync / async validation** — `validate()` / `validateAsync()`
|
|
45
|
-
- ✅ **Schema derivation** — `pick / omit / partial` to tailor schemas per endpoint
|
|
46
|
-
- ✅ **Database schemas** — export directly to MongoDB / MySQL / PostgreSQL
|
|
47
|
-
- ✅ **Field documentation** — auto-generate Markdown
|
|
48
|
-
- ✅ **Unified error model** — `ValidationError` + `I18nError`
|
|
49
|
-
- ✅ **Internationalization** — 5 built-in locales (zh-CN / en-US / ja-JP / es-ES / fr-FR), switchable at runtime
|
|
50
|
-
|
|
51
|
-
**5-minute tutorial**: [Quick Start](https://vextjs.github.io/schema-dsl/quick-start) | **Full docs**: [Online Documentation](https://vextjs.github.io/schema-dsl)
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## 🗺️ Documentation
|
|
56
|
-
|
|
57
|
-
**Getting started**:
|
|
58
|
-
- [Quick Start](https://vextjs.github.io/schema-dsl/quick-start) — up and running in 5 minutes
|
|
59
|
-
- [DSL Syntax Reference](#-dsl-syntax-reference) — syntax cheatsheet
|
|
60
|
-
- [FAQ](https://vextjs.github.io/schema-dsl/faq) — common questions
|
|
61
|
-
|
|
62
|
-
**Core features**:
|
|
63
|
-
- [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) — all validation scenarios
|
|
64
|
-
- [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) — schema reuse
|
|
65
|
-
- [Conditional Validation API](https://vextjs.github.io/schema-dsl/conditional-api) — dsl.if / dsl.match
|
|
66
|
-
- [Async Validation & Framework Integration](https://vextjs.github.io/schema-dsl/validate-async) — Express / Koa / Fastify
|
|
67
|
-
- [Error Handling & i18n](https://vextjs.github.io/schema-dsl/error-handling) — error model
|
|
68
|
-
|
|
69
|
-
**Export & integration**:
|
|
70
|
-
- [Export Guide](https://vextjs.github.io/schema-dsl/export-guide) — MongoDB / MySQL / PostgreSQL
|
|
71
|
-
- [TypeScript Guide](https://vextjs.github.io/schema-dsl/typescript-guide) — type inference and usage
|
|
72
|
-
- [Plugin System](https://vextjs.github.io/schema-dsl/plugin-system) — custom extensions
|
|
73
|
-
|
|
74
|
-
**Full docs**: [Online Documentation](https://vextjs.github.io/schema-dsl) · [Feature Index](https://vextjs.github.io/schema-dsl/FEATURE-INDEX)
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## ✨ Why schema-dsl?
|
|
79
|
-
|
|
80
|
-
### 🎯 Minimal DSL — 65% less code
|
|
81
|
-
|
|
82
|
-
<table>
|
|
83
|
-
<tr>
|
|
84
|
-
<td width="50%" valign="top">
|
|
85
|
-
|
|
86
|
-
**❌ Traditional approach** — verbose
|
|
87
|
-
|
|
88
|
-
```javascript
|
|
89
|
-
// Joi — requires 8 lines
|
|
90
|
-
const schema = Joi.object({
|
|
91
|
-
username: Joi.string()
|
|
92
|
-
.min(3).max(32).required(),
|
|
93
|
-
email: Joi.string()
|
|
94
|
-
.email().required(),
|
|
95
|
-
age: Joi.number()
|
|
96
|
-
.min(18).max(120)
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
</td>
|
|
101
|
-
<td width="50%" valign="top">
|
|
102
|
-
|
|
103
|
-
**✅ schema-dsl** — concise and clean
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
// just 3 lines
|
|
107
|
-
const schema = dsl({
|
|
108
|
-
username: 'string:3-32!',
|
|
109
|
-
email: 'email!',
|
|
110
|
-
age: 'number:18-120'
|
|
111
|
-
});
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
</td>
|
|
115
|
-
</tr>
|
|
116
|
-
</table>
|
|
117
|
-
|
|
118
|
-
### 💪 Full-featured
|
|
119
|
-
|
|
120
|
-
| Feature | schema-dsl | Notes |
|
|
121
|
-
|---------|:----------:|-------|
|
|
122
|
-
| **Basic validation** | ✅ | string, number, boolean, date, email, url, phone… |
|
|
123
|
-
| **Advanced validation** | ✅ | regex, custom functions, conditional branches, nested objects, arrays… |
|
|
124
|
-
| **Cross-type union** | ✅ | `types:email\|phone` — one field accepts multiple types |
|
|
125
|
-
| **Error messages** | ✅ | auto-translated + custom messages + field labels |
|
|
126
|
-
| **i18n business errors** | ✅ | `I18nError` with numeric error codes |
|
|
127
|
-
| **Database export** | ✅ | MongoDB / MySQL / PostgreSQL schema generation |
|
|
128
|
-
| **Documentation generation** | ✅ | Markdown field docs auto-generated |
|
|
129
|
-
| **TypeScript** | ✅ | Written in native TypeScript with full type inference |
|
|
130
|
-
| **Plugin system** | ✅ | Custom types / formats / validators |
|
|
131
|
-
| **Schema reuse** | ✅ | pick / omit / partial / extend |
|
|
132
|
-
|
|
133
|
-
### 🎨 One schema, many uses (unique capability)
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
import { dsl, exporters, SchemaUtils } from 'schema-dsl';
|
|
137
|
-
|
|
138
|
-
const userSchema = dsl({
|
|
139
|
-
id: 'uuid!',
|
|
140
|
-
username: 'string:3-32!',
|
|
141
|
-
email: 'email!',
|
|
142
|
-
password: 'string:8-64!',
|
|
143
|
-
age: 'number:18-120',
|
|
144
|
-
createdAt: 'string!'
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// 📋 derive scenario-specific schemas
|
|
148
|
-
const createSchema = SchemaUtils.omit(userSchema, ['id', 'createdAt']);
|
|
149
|
-
const updateSchema = SchemaUtils.partial(SchemaUtils.pick(userSchema, ['username', 'email']));
|
|
150
|
-
const publicSchema = SchemaUtils.omit(userSchema, ['password']);
|
|
151
|
-
|
|
152
|
-
// 🗄️ export the same schema to any database
|
|
153
|
-
const mongoSchema = new exporters.MongoDBExporter().export(userSchema);
|
|
154
|
-
const mysqlDDL = new exporters.MySQLExporter().export('users', userSchema);
|
|
155
|
-
const pgDDL = new exporters.PostgreSQLExporter().export('users', userSchema);
|
|
156
|
-
|
|
157
|
-
// 📝 generate field documentation from the same schema
|
|
158
|
-
const markdown = exporters.MarkdownExporter.export(userSchema, { title: 'User Field Reference' });
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
> ⚠️ SQL exporters only accept `anyOf` / `oneOf` when every branch resolves to the **same** SQL column type (for example `ipv4 | ipv6`). Ambiguous unions such as `string | number` now throw an explicit error instead of silently choosing the first branch.
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
## 📦 Installation
|
|
166
|
-
|
|
167
|
-
```bash
|
|
168
|
-
npm install schema-dsl
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
**Runtime requirement**: Node.js >= 18.0.0
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
## 🚀 Quick Start
|
|
176
|
-
|
|
177
|
-
### 1. Basic validation
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
import { dsl, validate } from 'schema-dsl';
|
|
181
|
-
|
|
182
|
-
const userSchema = dsl({
|
|
183
|
-
username: 'string:3-32!',
|
|
184
|
-
email: 'email!',
|
|
185
|
-
age: 'number:18-120',
|
|
186
|
-
role: 'admin|user|guest',
|
|
187
|
-
tags: 'array<string>'
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// ✅ validation passed
|
|
191
|
-
const result = validate(userSchema, {
|
|
192
|
-
username: 'john_doe',
|
|
193
|
-
email: 'john@example.com',
|
|
194
|
-
age: 25,
|
|
195
|
-
role: 'user',
|
|
196
|
-
tags: ['verified']
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
console.log(result.valid); // true
|
|
200
|
-
console.log(result.data); // validated data
|
|
201
|
-
|
|
202
|
-
// ❌ validation failed
|
|
203
|
-
const bad = validate(userSchema, { username: 'ab', email: 'not-email' });
|
|
204
|
-
console.log(bad.errors);
|
|
205
|
-
// [
|
|
206
|
-
// { path: 'username', message: 'username must be at least 3 characters' },
|
|
207
|
-
// { path: 'email', message: 'email must be a valid email address' }
|
|
208
|
-
// ]
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### 2. Async validation + Express integration
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
import { dsl, validateAsync, ValidationError } from 'schema-dsl';
|
|
215
|
-
|
|
216
|
-
const createUserSchema = dsl({
|
|
217
|
-
username: 'string:3-32!',
|
|
218
|
-
email: 'email!',
|
|
219
|
-
password: 'string:8-32!'
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
app.post('/api/users', async (req, res, next) => {
|
|
223
|
-
try {
|
|
224
|
-
// throws ValidationError automatically on failure
|
|
225
|
-
const validData = await validateAsync(createUserSchema, req.body);
|
|
226
|
-
const user = await db.users.create(validData);
|
|
227
|
-
res.json({ success: true, data: user });
|
|
228
|
-
} catch (error) {
|
|
229
|
-
next(error);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// global error handler
|
|
234
|
-
app.use((error, req, res, next) => {
|
|
235
|
-
if (error instanceof ValidationError) {
|
|
236
|
-
return res.status(400).json({ success: false, errors: error.errors });
|
|
237
|
-
}
|
|
238
|
-
next(error);
|
|
239
|
-
});
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### 3. Schema reuse (create / update / public)
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
import { dsl, SchemaUtils } from 'schema-dsl';
|
|
246
|
-
|
|
247
|
-
const userSchema = dsl({
|
|
248
|
-
id: 'uuid!',
|
|
249
|
-
username: 'string:3-32!',
|
|
250
|
-
email: 'email!',
|
|
251
|
-
password: 'string:8-64!',
|
|
252
|
-
createdAt: 'string!'
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// create endpoint: remove server-generated fields
|
|
256
|
-
const createSchema = SchemaUtils.omit(userSchema, ['id', 'createdAt']);
|
|
257
|
-
|
|
258
|
-
// update endpoint: pick editable fields, all optional
|
|
259
|
-
const updateSchema = SchemaUtils.partial(
|
|
260
|
-
SchemaUtils.pick(userSchema, ['username', 'email'])
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
// public response: hide sensitive fields
|
|
264
|
-
const publicSchema = SchemaUtils.omit(userSchema, ['password']);
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
### 4. Database schema export
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
import { dsl, exporters } from 'schema-dsl';
|
|
271
|
-
|
|
272
|
-
const productSchema = dsl({
|
|
273
|
-
name: 'string:1-100!',
|
|
274
|
-
price: 'number:>0!',
|
|
275
|
-
stock: 'integer:0-!',
|
|
276
|
-
category: 'string!',
|
|
277
|
-
createdAt: 'datetime!'
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
// MongoDB $jsonSchema (for db.createCollection() document validation; not a Mongoose model schema)
|
|
281
|
-
const mongoSchema = new exporters.MongoDBExporter().export(productSchema);
|
|
282
|
-
/*
|
|
283
|
-
{
|
|
284
|
-
$jsonSchema: {
|
|
285
|
-
bsonType: 'object',
|
|
286
|
-
properties: {
|
|
287
|
-
name: { bsonType: 'string', minLength: 1, maxLength: 100 },
|
|
288
|
-
price: { bsonType: 'double', minimum: 0 },
|
|
289
|
-
stock: { bsonType: 'int', minimum: 0 },
|
|
290
|
-
category: { bsonType: 'string' },
|
|
291
|
-
createdAt: { bsonType: 'string' }
|
|
292
|
-
},
|
|
293
|
-
required: ['name', 'price', 'stock', 'category', 'createdAt']
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
*/
|
|
297
|
-
|
|
298
|
-
// MySQL DDL
|
|
299
|
-
const mysqlDDL = new exporters.MySQLExporter().export('products', productSchema);
|
|
300
|
-
/*
|
|
301
|
-
CREATE TABLE `products` (
|
|
302
|
-
`name` VARCHAR(100) NOT NULL,
|
|
303
|
-
`price` DECIMAL(10, 2) NOT NULL,
|
|
304
|
-
`stock` INT NOT NULL,
|
|
305
|
-
`category` VARCHAR(255) NOT NULL,
|
|
306
|
-
`createdAt` DATETIME NOT NULL
|
|
307
|
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
308
|
-
*/
|
|
309
|
-
|
|
310
|
-
// Markdown field documentation
|
|
311
|
-
const markdown = exporters.MarkdownExporter.export(productSchema, { title: 'Product Field Reference' });
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
## 🗒️ Feature Overview
|
|
317
|
-
|
|
318
|
-
### Common use cases
|
|
319
|
-
|
|
320
|
-
| Use case | API | Docs |
|
|
321
|
-
|----------|-----|------|
|
|
322
|
-
| API parameter validation | `validateAsync` + `ValidationError` | [Async Validation](https://vextjs.github.io/schema-dsl/validate-async) |
|
|
323
|
-
| Form / script validation | `validate()` | [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) |
|
|
324
|
-
| Batch data validation | `SchemaUtils.validateBatch()` | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
325
|
-
| create / update derivation | `pick / omit / partial` | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
326
|
-
| Database table creation | `MongoDBExporter / MySQLExporter` | [Export Guide](https://vextjs.github.io/schema-dsl/export-guide) |
|
|
327
|
-
| Field documentation | `MarkdownExporter` | [Export Guide](https://vextjs.github.io/schema-dsl/export-guide) |
|
|
328
|
-
| Multilingual API errors | `I18nError` | [Error Handling](https://vextjs.github.io/schema-dsl/error-handling) |
|
|
329
|
-
| Conditional / dynamic rules | `dsl.if()` / `dsl.match()` | [Conditional API](https://vextjs.github.io/schema-dsl/conditional-api) |
|
|
330
|
-
| Custom type extensions | `PluginManager` | [Plugin System](https://vextjs.github.io/schema-dsl/plugin-system) |
|
|
331
|
-
|
|
332
|
-
---
|
|
333
|
-
|
|
334
|
-
## 📖 DSL Syntax Reference
|
|
335
|
-
|
|
336
|
-
### Basic types
|
|
337
|
-
|
|
338
|
-
```typescript
|
|
339
|
-
dsl({
|
|
340
|
-
// string
|
|
341
|
-
name: 'string!', // required
|
|
342
|
-
code: 'string:6', // exact length 6
|
|
343
|
-
bio: 'string:-500', // max length 500
|
|
344
|
-
username: 'string:3-32', // length range 3–32
|
|
345
|
-
|
|
346
|
-
// number
|
|
347
|
-
age: 'number:18-120', // range 18–120
|
|
348
|
-
score: 'integer:0-100', // integer 0–100
|
|
349
|
-
price: 'number:>0', // strictly greater than 0
|
|
350
|
-
level: 'number:>=1', // greater than or equal to 1
|
|
351
|
-
|
|
352
|
-
// enum
|
|
353
|
-
status: 'active|inactive|pending', // string enum
|
|
354
|
-
tier: 'enum:number:1|2|3', // numeric enum
|
|
355
|
-
|
|
356
|
-
// array
|
|
357
|
-
tags: 'array<string>', // string array
|
|
358
|
-
items: 'array:1-10<number>', // 1–10 numeric elements
|
|
359
|
-
|
|
360
|
-
// boolean
|
|
361
|
-
active: 'boolean!',
|
|
362
|
-
|
|
363
|
-
// union type
|
|
364
|
-
contact: 'types:email|phone!', // email or phone, required
|
|
365
|
-
price2: 'types:number:0-|string', // number or string
|
|
366
|
-
})
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Built-in formats
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
dsl({
|
|
373
|
-
email: 'email!', // email address
|
|
374
|
-
website: 'url!', // URL
|
|
375
|
-
birthday: 'date!', // YYYY-MM-DD
|
|
376
|
-
createdAt: 'datetime!', // ISO 8601
|
|
377
|
-
userId: 'uuid!', // UUID
|
|
378
|
-
phone: 'phone:cn!', // Chinese mobile number
|
|
379
|
-
idCard: 'idCard:cn!', // Chinese national ID
|
|
380
|
-
slug: 'slug:3-100!', // URL-friendly string
|
|
381
|
-
})
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
### Fluent chain API (recommended for TypeScript)
|
|
385
|
-
|
|
386
|
-
```typescript
|
|
387
|
-
import { dsl } from 'schema-dsl';
|
|
388
|
-
|
|
389
|
-
const schema = dsl({
|
|
390
|
-
username: dsl('string:3-32!')
|
|
391
|
-
.username()
|
|
392
|
-
.label('username')
|
|
393
|
-
.messages({ required: 'Username is required' }),
|
|
394
|
-
|
|
395
|
-
email: dsl('email!').label('email address'),
|
|
396
|
-
|
|
397
|
-
phone: dsl('string:11!')
|
|
398
|
-
.pattern(/^1[3-9]\d{9}$/)
|
|
399
|
-
.label('phone number'),
|
|
400
|
-
});
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### Conditional validation
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
406
|
-
// dsl.match — route to different rules based on a field value
|
|
407
|
-
const contactSchema = dsl({
|
|
408
|
-
type: 'email|phone|wechat',
|
|
409
|
-
contact: dsl.match('type', {
|
|
410
|
-
email: 'email!',
|
|
411
|
-
phone: 'string:11!',
|
|
412
|
-
wechat: 'string:6-20!',
|
|
413
|
-
})
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
// dsl.if — simple conditional branch
|
|
417
|
-
const orderSchema = dsl({
|
|
418
|
-
isVip: 'boolean!',
|
|
419
|
-
discount: dsl.if('isVip', 'number:10-50!', 'number:0-10')
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
// dsl.if chain assertion
|
|
423
|
-
dsl.if(d => !d.account)
|
|
424
|
-
.message('Account not found')
|
|
425
|
-
.and(d => d.account.balance < amount)
|
|
426
|
-
.message('Insufficient balance')
|
|
427
|
-
.assert(data);
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
---
|
|
431
|
-
|
|
432
|
-
## 🌍 Internationalization
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
import { dsl, validate, Locale, I18nError } from 'schema-dsl';
|
|
436
|
-
|
|
437
|
-
// built-in locales: zh-CN / en-US / ja-JP / es-ES / fr-FR (auto-loaded, no configuration needed)
|
|
438
|
-
const result = validate(schema, data, { locale: 'en-US' });
|
|
439
|
-
// error messages automatically use the specified locale
|
|
440
|
-
|
|
441
|
-
// register a custom locale
|
|
442
|
-
Locale.addLocale('zh-CN', {
|
|
443
|
-
'user.notFound': 'User not found',
|
|
444
|
-
'user.forbidden': { code: 40003, message: 'Access forbidden' },
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// throw i18n business errors
|
|
448
|
-
I18nError.assert(user, 'user.notFound'); // auto-throw when user is falsy
|
|
449
|
-
I18nError.throw('user.forbidden', {}, 403); // throw directly
|
|
450
|
-
I18nError.assert(ok, 'user.notFound', {}, 404, locale); // specify locale at runtime
|
|
451
|
-
|
|
452
|
-
// errors carry a numeric code; frontend can branch on it
|
|
453
|
-
try {
|
|
454
|
-
await api.getUser(id);
|
|
455
|
-
} catch (error) {
|
|
456
|
-
switch (error.code) {
|
|
457
|
-
case 40003: showForbiddenPage(); break;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
---
|
|
463
|
-
|
|
464
|
-
## 🔌 Plugin System
|
|
465
|
-
|
|
466
|
-
```typescript
|
|
467
|
-
import { PluginManager, Validator, dsl } from 'schema-dsl';
|
|
468
|
-
|
|
469
|
-
const pluginManager = new PluginManager();
|
|
470
|
-
|
|
471
|
-
// register a custom format plugin (must provide an install function)
|
|
472
|
-
pluginManager.register({
|
|
473
|
-
name: 'extra-formats',
|
|
474
|
-
install(core) {
|
|
475
|
-
const validator = core as Validator;
|
|
476
|
-
// register custom formats on the Validator instance via addFormat
|
|
477
|
-
validator.addFormat('hex-color', {
|
|
478
|
-
validate: (v: string) => /^#[0-9A-F]{6}$/i.test(v)
|
|
479
|
-
});
|
|
480
|
-
validator.addFormat('mac-address', {
|
|
481
|
-
validate: (v: string) => /^([0-9A-F]{2}:){5}[0-9A-F]{2}$/i.test(v)
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
// create a Validator and install plugins
|
|
487
|
-
const validator = new Validator();
|
|
488
|
-
pluginManager.install(validator);
|
|
489
|
-
|
|
490
|
-
// use the custom formats in a schema
|
|
491
|
-
const schema = dsl({ color: 'hex-color!', mac: 'mac-address' });
|
|
492
|
-
const result = validator.validate(schema, { color: '#FF5733', mac: '00:1A:2B:3C:4D:5E' });
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
---
|
|
496
|
-
|
|
497
|
-
## 🔧 Core API Reference
|
|
498
|
-
|
|
499
|
-
| API | Purpose | Returns | Docs |
|
|
500
|
-
|-----|---------|---------|------|
|
|
501
|
-
| `dsl(schema)` | Create a schema | Schema object | [DSL Syntax](https://vextjs.github.io/schema-dsl/dsl-syntax) |
|
|
502
|
-
| `validate(schema, data)` | Synchronous validation | `{ valid, errors, data }` | [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) |
|
|
503
|
-
| `validateAsync(schema, data)` | Asynchronous validation | Promise (throws on failure) | [Async Validation](https://vextjs.github.io/schema-dsl/validate-async) |
|
|
504
|
-
| `SchemaUtils.pick()` | Select fields | New schema | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
505
|
-
| `SchemaUtils.omit()` | Exclude fields | New schema | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
506
|
-
| `SchemaUtils.partial()` | Make all fields optional | New schema | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
507
|
-
| `dsl.if(condition)` | Conditional validation | ConditionalBuilder | [Conditional API](https://vextjs.github.io/schema-dsl/conditional-api) |
|
|
508
|
-
| `dsl.match(field, map)` | Branch validation | ConditionalBuilder | [Conditional API](https://vextjs.github.io/schema-dsl/conditional-api) |
|
|
509
|
-
| `I18nError.throw()` | Throw an i18n error | never | [Error Handling](https://vextjs.github.io/schema-dsl/error-handling) |
|
|
510
|
-
| `I18nError.assert()` | Assert then throw | void | [Error Handling](https://vextjs.github.io/schema-dsl/error-handling) |
|
|
511
|
-
|
|
512
|
-
---
|
|
513
|
-
|
|
514
|
-
## 📝 TypeScript Usage
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
import { dsl, validateAsync, ValidationError } from 'schema-dsl';
|
|
518
|
-
|
|
519
|
-
// ✅ wrap strings with dsl() in TypeScript for full type inference
|
|
520
|
-
const userSchema = dsl({
|
|
521
|
-
username: dsl('string:3-32!').label('username'),
|
|
522
|
-
email: dsl('email!').label('email'),
|
|
523
|
-
age: dsl('number:18-100').label('age')
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
try {
|
|
527
|
-
const validData = await validateAsync(userSchema, payload);
|
|
528
|
-
// validData has full type inference
|
|
529
|
-
} catch (error) {
|
|
530
|
-
if (error instanceof ValidationError) {
|
|
531
|
-
error.errors.forEach(e => console.log(`${e.path}: ${e.message}`));
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
> **Note**: In TypeScript projects, wrap strings with `dsl('...')` to get type inference. In JavaScript projects you can pass strings directly.
|
|
537
|
-
> See the [TypeScript Guide](https://vextjs.github.io/schema-dsl/typescript-guide) for details.
|
|
538
|
-
|
|
539
|
-
---
|
|
540
|
-
|
|
541
|
-
## 🛠️ Development
|
|
542
|
-
|
|
543
|
-
```bash
|
|
544
|
-
npm run build # compile TypeScript
|
|
545
|
-
npm run test # run tests
|
|
546
|
-
npm run typecheck # type check
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
Local documentation preview:
|
|
550
|
-
|
|
551
|
-
```bash
|
|
552
|
-
cd website
|
|
553
|
-
npm run dev
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
---
|
|
557
|
-
|
|
558
|
-
## 🤝 Contributing
|
|
559
|
-
|
|
560
|
-
```bash
|
|
561
|
-
git clone https://github.com/vextjs/schema-dsl.git
|
|
562
|
-
cd schema-dsl
|
|
563
|
-
npm install
|
|
564
|
-
npm test
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
See [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
568
|
-
|
|
569
|
-
---
|
|
570
|
-
|
|
571
|
-
## 🔗 Links
|
|
572
|
-
|
|
573
|
-
### 📖 Core documentation
|
|
574
|
-
- [Quick Start](https://vextjs.github.io/schema-dsl/quick-start) — up and running in 5 minutes
|
|
575
|
-
- [DSL Syntax Guide](https://vextjs.github.io/schema-dsl/dsl-syntax) — complete syntax reference
|
|
576
|
-
- [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) — advanced validation techniques
|
|
577
|
-
- [API Reference](https://vextjs.github.io/schema-dsl/api-reference) — complete API docs
|
|
578
|
-
- [TypeScript Guide](https://vextjs.github.io/schema-dsl/typescript-guide) — required reading for TS users
|
|
579
|
-
- [Best Practices](https://vextjs.github.io/schema-dsl/best-practices) — avoid common pitfalls
|
|
580
|
-
- [Troubleshooting](https://vextjs.github.io/schema-dsl/troubleshooting) — diagnosing issues
|
|
581
|
-
|
|
582
|
-
### 🎯 Feature documentation
|
|
583
|
-
- [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils)
|
|
584
|
-
- [Conditional Validation API](https://vextjs.github.io/schema-dsl/conditional-api)
|
|
585
|
-
- [Async Validation](https://vextjs.github.io/schema-dsl/validate-async)
|
|
586
|
-
- [Error Handling & i18n](https://vextjs.github.io/schema-dsl/error-handling)
|
|
587
|
-
- [Union Types](https://vextjs.github.io/schema-dsl/union-types)
|
|
588
|
-
- [Enum Types](https://vextjs.github.io/schema-dsl/enum)
|
|
589
|
-
|
|
590
|
-
### 🗄️ Export & integration
|
|
591
|
-
- [Export Guide](https://vextjs.github.io/schema-dsl/export-guide)
|
|
592
|
-
- [MongoDB Exporter](https://vextjs.github.io/schema-dsl/mongodb-exporter)
|
|
593
|
-
- [MySQL Exporter](https://vextjs.github.io/schema-dsl/mysql-exporter)
|
|
594
|
-
- [PostgreSQL Exporter](https://vextjs.github.io/schema-dsl/postgresql-exporter)
|
|
595
|
-
- [Markdown Exporter](https://vextjs.github.io/schema-dsl/markdown-exporter)
|
|
596
|
-
- [⚠️ Export Limitations](https://vextjs.github.io/schema-dsl/export-limitations)
|
|
597
|
-
|
|
598
|
-
### 💻 Examples
|
|
599
|
-
- [quick-start.ts](./examples/docs/quick-start.ts) — basic usage and registration form
|
|
600
|
-
- [validate-async.ts](./examples/docs/validate-async.ts) — async validation and `ValidationError` handling
|
|
601
|
-
- [export-guide.ts](./examples/docs/export-guide.ts) — database export overview
|
|
602
|
-
- [error-handling.ts](./examples/docs/error-handling.ts) — field errors and business error handling
|
|
603
|
-
- [plugin-system.ts](./examples/docs/plugin-system.ts) — plugin system and hooks
|
|
604
|
-
|
|
605
|
-
### 📝 Changelog & contributing
|
|
606
|
-
- [Changelog](./CHANGELOG.md)
|
|
607
|
-
- [Contributing Guide](./CONTRIBUTING.md)
|
|
608
|
-
- [Security Policy](./SECURITY.md)
|
|
609
|
-
|
|
610
|
-
---
|
|
611
|
-
|
|
612
|
-
## 📄 License
|
|
613
|
-
|
|
614
|
-
[MIT](./LICENSE)
|
|
615
|
-
|
|
616
|
-
---
|
|
617
|
-
|
|
618
|
-
<div align="center">
|
|
619
|
-
|
|
620
|
-
If this project is useful to you, please consider giving it a Star ⭐
|
|
621
|
-
|
|
622
|
-
Made with ❤️ by the schema-dsl team
|
|
623
|
-
|
|
624
|
-
</div>
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🎯 schema-dsl
|
|
4
|
+
|
|
5
|
+
**Declare field rules with the simplest DSL — let one schema drive validation, derivation, export, and documentation.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/schema-dsl)
|
|
8
|
+
[](https://www.npmjs.com/package/schema-dsl)
|
|
9
|
+
[](https://github.com/vextjs/schema-dsl/actions)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](https://opensource.org/licenses/MIT)
|
|
12
|
+
|
|
13
|
+
[Quick Start](#-quick-start) · [Documentation](https://vextjs.github.io/schema-dsl) · [Feature Overview](#-feature-overview) · [Examples](./examples)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install schema-dsl
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## ⚡ TL;DR (30-second intro)
|
|
24
|
+
|
|
25
|
+
**What is schema-dsl?**
|
|
26
|
+
|
|
27
|
+
Write field rules like this:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { dsl, validate } from 'schema-dsl';
|
|
31
|
+
|
|
32
|
+
const userSchema = dsl({
|
|
33
|
+
username: 'string:3-32!',
|
|
34
|
+
email: 'email!',
|
|
35
|
+
role: 'admin|user|guest',
|
|
36
|
+
contact: 'types:email|phone'
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const result = validate(userSchema, req.body);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then that **same set of rules** continues to power:
|
|
43
|
+
|
|
44
|
+
- ✅ **Sync / async validation** — `validate()` / `validateAsync()`
|
|
45
|
+
- ✅ **Schema derivation** — `pick / omit / partial` to tailor schemas per endpoint
|
|
46
|
+
- ✅ **Database schemas** — export directly to MongoDB / MySQL / PostgreSQL
|
|
47
|
+
- ✅ **Field documentation** — auto-generate Markdown
|
|
48
|
+
- ✅ **Unified error model** — `ValidationError` + `I18nError`
|
|
49
|
+
- ✅ **Internationalization** — 5 built-in locales (zh-CN / en-US / ja-JP / es-ES / fr-FR), switchable at runtime
|
|
50
|
+
|
|
51
|
+
**5-minute tutorial**: [Quick Start](https://vextjs.github.io/schema-dsl/quick-start) | **Full docs**: [Online Documentation](https://vextjs.github.io/schema-dsl)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🗺️ Documentation
|
|
56
|
+
|
|
57
|
+
**Getting started**:
|
|
58
|
+
- [Quick Start](https://vextjs.github.io/schema-dsl/quick-start) — up and running in 5 minutes
|
|
59
|
+
- [DSL Syntax Reference](#-dsl-syntax-reference) — syntax cheatsheet
|
|
60
|
+
- [FAQ](https://vextjs.github.io/schema-dsl/faq) — common questions
|
|
61
|
+
|
|
62
|
+
**Core features**:
|
|
63
|
+
- [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) — all validation scenarios
|
|
64
|
+
- [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) — schema reuse
|
|
65
|
+
- [Conditional Validation API](https://vextjs.github.io/schema-dsl/conditional-api) — dsl.if / dsl.match
|
|
66
|
+
- [Async Validation & Framework Integration](https://vextjs.github.io/schema-dsl/validate-async) — Express / Koa / Fastify
|
|
67
|
+
- [Error Handling & i18n](https://vextjs.github.io/schema-dsl/error-handling) — error model
|
|
68
|
+
|
|
69
|
+
**Export & integration**:
|
|
70
|
+
- [Export Guide](https://vextjs.github.io/schema-dsl/export-guide) — MongoDB / MySQL / PostgreSQL
|
|
71
|
+
- [TypeScript Guide](https://vextjs.github.io/schema-dsl/typescript-guide) — type inference and usage
|
|
72
|
+
- [Plugin System](https://vextjs.github.io/schema-dsl/plugin-system) — custom extensions
|
|
73
|
+
|
|
74
|
+
**Full docs**: [Online Documentation](https://vextjs.github.io/schema-dsl) · [Feature Index](https://vextjs.github.io/schema-dsl/FEATURE-INDEX)
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## ✨ Why schema-dsl?
|
|
79
|
+
|
|
80
|
+
### 🎯 Minimal DSL — 65% less code
|
|
81
|
+
|
|
82
|
+
<table>
|
|
83
|
+
<tr>
|
|
84
|
+
<td width="50%" valign="top">
|
|
85
|
+
|
|
86
|
+
**❌ Traditional approach** — verbose
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// Joi — requires 8 lines
|
|
90
|
+
const schema = Joi.object({
|
|
91
|
+
username: Joi.string()
|
|
92
|
+
.min(3).max(32).required(),
|
|
93
|
+
email: Joi.string()
|
|
94
|
+
.email().required(),
|
|
95
|
+
age: Joi.number()
|
|
96
|
+
.min(18).max(120)
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
</td>
|
|
101
|
+
<td width="50%" valign="top">
|
|
102
|
+
|
|
103
|
+
**✅ schema-dsl** — concise and clean
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// just 3 lines
|
|
107
|
+
const schema = dsl({
|
|
108
|
+
username: 'string:3-32!',
|
|
109
|
+
email: 'email!',
|
|
110
|
+
age: 'number:18-120'
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
</td>
|
|
115
|
+
</tr>
|
|
116
|
+
</table>
|
|
117
|
+
|
|
118
|
+
### 💪 Full-featured
|
|
119
|
+
|
|
120
|
+
| Feature | schema-dsl | Notes |
|
|
121
|
+
|---------|:----------:|-------|
|
|
122
|
+
| **Basic validation** | ✅ | string, number, boolean, date, email, url, phone… |
|
|
123
|
+
| **Advanced validation** | ✅ | regex, custom functions, conditional branches, nested objects, arrays… |
|
|
124
|
+
| **Cross-type union** | ✅ | `types:email\|phone` — one field accepts multiple types |
|
|
125
|
+
| **Error messages** | ✅ | auto-translated + custom messages + field labels |
|
|
126
|
+
| **i18n business errors** | ✅ | `I18nError` with numeric error codes |
|
|
127
|
+
| **Database export** | ✅ | MongoDB / MySQL / PostgreSQL schema generation |
|
|
128
|
+
| **Documentation generation** | ✅ | Markdown field docs auto-generated |
|
|
129
|
+
| **TypeScript** | ✅ | Written in native TypeScript with full type inference |
|
|
130
|
+
| **Plugin system** | ✅ | Custom types / formats / validators |
|
|
131
|
+
| **Schema reuse** | ✅ | pick / omit / partial / extend |
|
|
132
|
+
|
|
133
|
+
### 🎨 One schema, many uses (unique capability)
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { dsl, exporters, SchemaUtils } from 'schema-dsl';
|
|
137
|
+
|
|
138
|
+
const userSchema = dsl({
|
|
139
|
+
id: 'uuid!',
|
|
140
|
+
username: 'string:3-32!',
|
|
141
|
+
email: 'email!',
|
|
142
|
+
password: 'string:8-64!',
|
|
143
|
+
age: 'number:18-120',
|
|
144
|
+
createdAt: 'string!'
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// 📋 derive scenario-specific schemas
|
|
148
|
+
const createSchema = SchemaUtils.omit(userSchema, ['id', 'createdAt']);
|
|
149
|
+
const updateSchema = SchemaUtils.partial(SchemaUtils.pick(userSchema, ['username', 'email']));
|
|
150
|
+
const publicSchema = SchemaUtils.omit(userSchema, ['password']);
|
|
151
|
+
|
|
152
|
+
// 🗄️ export the same schema to any database
|
|
153
|
+
const mongoSchema = new exporters.MongoDBExporter().export(userSchema);
|
|
154
|
+
const mysqlDDL = new exporters.MySQLExporter().export('users', userSchema);
|
|
155
|
+
const pgDDL = new exporters.PostgreSQLExporter().export('users', userSchema);
|
|
156
|
+
|
|
157
|
+
// 📝 generate field documentation from the same schema
|
|
158
|
+
const markdown = exporters.MarkdownExporter.export(userSchema, { title: 'User Field Reference' });
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> ⚠️ SQL exporters only accept `anyOf` / `oneOf` when every branch resolves to the **same** SQL column type (for example `ipv4 | ipv6`). Ambiguous unions such as `string | number` now throw an explicit error instead of silently choosing the first branch.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 📦 Installation
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npm install schema-dsl
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Runtime requirement**: Node.js >= 18.0.0
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🚀 Quick Start
|
|
176
|
+
|
|
177
|
+
### 1. Basic validation
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { dsl, validate } from 'schema-dsl';
|
|
181
|
+
|
|
182
|
+
const userSchema = dsl({
|
|
183
|
+
username: 'string:3-32!',
|
|
184
|
+
email: 'email!',
|
|
185
|
+
age: 'number:18-120',
|
|
186
|
+
role: 'admin|user|guest',
|
|
187
|
+
tags: 'array<string>'
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ✅ validation passed
|
|
191
|
+
const result = validate(userSchema, {
|
|
192
|
+
username: 'john_doe',
|
|
193
|
+
email: 'john@example.com',
|
|
194
|
+
age: 25,
|
|
195
|
+
role: 'user',
|
|
196
|
+
tags: ['verified']
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
console.log(result.valid); // true
|
|
200
|
+
console.log(result.data); // validated data
|
|
201
|
+
|
|
202
|
+
// ❌ validation failed
|
|
203
|
+
const bad = validate(userSchema, { username: 'ab', email: 'not-email' });
|
|
204
|
+
console.log(bad.errors);
|
|
205
|
+
// [
|
|
206
|
+
// { path: 'username', message: 'username must be at least 3 characters' },
|
|
207
|
+
// { path: 'email', message: 'email must be a valid email address' }
|
|
208
|
+
// ]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 2. Async validation + Express integration
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { dsl, validateAsync, ValidationError } from 'schema-dsl';
|
|
215
|
+
|
|
216
|
+
const createUserSchema = dsl({
|
|
217
|
+
username: 'string:3-32!',
|
|
218
|
+
email: 'email!',
|
|
219
|
+
password: 'string:8-32!'
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
app.post('/api/users', async (req, res, next) => {
|
|
223
|
+
try {
|
|
224
|
+
// throws ValidationError automatically on failure
|
|
225
|
+
const validData = await validateAsync(createUserSchema, req.body);
|
|
226
|
+
const user = await db.users.create(validData);
|
|
227
|
+
res.json({ success: true, data: user });
|
|
228
|
+
} catch (error) {
|
|
229
|
+
next(error);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// global error handler
|
|
234
|
+
app.use((error, req, res, next) => {
|
|
235
|
+
if (error instanceof ValidationError) {
|
|
236
|
+
return res.status(400).json({ success: false, errors: error.errors });
|
|
237
|
+
}
|
|
238
|
+
next(error);
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 3. Schema reuse (create / update / public)
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { dsl, SchemaUtils } from 'schema-dsl';
|
|
246
|
+
|
|
247
|
+
const userSchema = dsl({
|
|
248
|
+
id: 'uuid!',
|
|
249
|
+
username: 'string:3-32!',
|
|
250
|
+
email: 'email!',
|
|
251
|
+
password: 'string:8-64!',
|
|
252
|
+
createdAt: 'string!'
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// create endpoint: remove server-generated fields
|
|
256
|
+
const createSchema = SchemaUtils.omit(userSchema, ['id', 'createdAt']);
|
|
257
|
+
|
|
258
|
+
// update endpoint: pick editable fields, all optional
|
|
259
|
+
const updateSchema = SchemaUtils.partial(
|
|
260
|
+
SchemaUtils.pick(userSchema, ['username', 'email'])
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// public response: hide sensitive fields
|
|
264
|
+
const publicSchema = SchemaUtils.omit(userSchema, ['password']);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 4. Database schema export
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import { dsl, exporters } from 'schema-dsl';
|
|
271
|
+
|
|
272
|
+
const productSchema = dsl({
|
|
273
|
+
name: 'string:1-100!',
|
|
274
|
+
price: 'number:>0!',
|
|
275
|
+
stock: 'integer:0-!',
|
|
276
|
+
category: 'string!',
|
|
277
|
+
createdAt: 'datetime!'
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// MongoDB $jsonSchema (for db.createCollection() document validation; not a Mongoose model schema)
|
|
281
|
+
const mongoSchema = new exporters.MongoDBExporter().export(productSchema);
|
|
282
|
+
/*
|
|
283
|
+
{
|
|
284
|
+
$jsonSchema: {
|
|
285
|
+
bsonType: 'object',
|
|
286
|
+
properties: {
|
|
287
|
+
name: { bsonType: 'string', minLength: 1, maxLength: 100 },
|
|
288
|
+
price: { bsonType: 'double', minimum: 0 },
|
|
289
|
+
stock: { bsonType: 'int', minimum: 0 },
|
|
290
|
+
category: { bsonType: 'string' },
|
|
291
|
+
createdAt: { bsonType: 'string' }
|
|
292
|
+
},
|
|
293
|
+
required: ['name', 'price', 'stock', 'category', 'createdAt']
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
*/
|
|
297
|
+
|
|
298
|
+
// MySQL DDL
|
|
299
|
+
const mysqlDDL = new exporters.MySQLExporter().export('products', productSchema);
|
|
300
|
+
/*
|
|
301
|
+
CREATE TABLE `products` (
|
|
302
|
+
`name` VARCHAR(100) NOT NULL,
|
|
303
|
+
`price` DECIMAL(10, 2) NOT NULL,
|
|
304
|
+
`stock` INT NOT NULL,
|
|
305
|
+
`category` VARCHAR(255) NOT NULL,
|
|
306
|
+
`createdAt` DATETIME NOT NULL
|
|
307
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
308
|
+
*/
|
|
309
|
+
|
|
310
|
+
// Markdown field documentation
|
|
311
|
+
const markdown = exporters.MarkdownExporter.export(productSchema, { title: 'Product Field Reference' });
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## 🗒️ Feature Overview
|
|
317
|
+
|
|
318
|
+
### Common use cases
|
|
319
|
+
|
|
320
|
+
| Use case | API | Docs |
|
|
321
|
+
|----------|-----|------|
|
|
322
|
+
| API parameter validation | `validateAsync` + `ValidationError` | [Async Validation](https://vextjs.github.io/schema-dsl/validate-async) |
|
|
323
|
+
| Form / script validation | `validate()` | [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) |
|
|
324
|
+
| Batch data validation | `SchemaUtils.validateBatch()` | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
325
|
+
| create / update derivation | `pick / omit / partial` | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
326
|
+
| Database table creation | `MongoDBExporter / MySQLExporter` | [Export Guide](https://vextjs.github.io/schema-dsl/export-guide) |
|
|
327
|
+
| Field documentation | `MarkdownExporter` | [Export Guide](https://vextjs.github.io/schema-dsl/export-guide) |
|
|
328
|
+
| Multilingual API errors | `I18nError` | [Error Handling](https://vextjs.github.io/schema-dsl/error-handling) |
|
|
329
|
+
| Conditional / dynamic rules | `dsl.if()` / `dsl.match()` | [Conditional API](https://vextjs.github.io/schema-dsl/conditional-api) |
|
|
330
|
+
| Custom type extensions | `PluginManager` | [Plugin System](https://vextjs.github.io/schema-dsl/plugin-system) |
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 📖 DSL Syntax Reference
|
|
335
|
+
|
|
336
|
+
### Basic types
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
dsl({
|
|
340
|
+
// string
|
|
341
|
+
name: 'string!', // required
|
|
342
|
+
code: 'string:6', // exact length 6
|
|
343
|
+
bio: 'string:-500', // max length 500
|
|
344
|
+
username: 'string:3-32', // length range 3–32
|
|
345
|
+
|
|
346
|
+
// number
|
|
347
|
+
age: 'number:18-120', // range 18–120
|
|
348
|
+
score: 'integer:0-100', // integer 0–100
|
|
349
|
+
price: 'number:>0', // strictly greater than 0
|
|
350
|
+
level: 'number:>=1', // greater than or equal to 1
|
|
351
|
+
|
|
352
|
+
// enum
|
|
353
|
+
status: 'active|inactive|pending', // string enum
|
|
354
|
+
tier: 'enum:number:1|2|3', // numeric enum
|
|
355
|
+
|
|
356
|
+
// array
|
|
357
|
+
tags: 'array<string>', // string array
|
|
358
|
+
items: 'array:1-10<number>', // 1–10 numeric elements
|
|
359
|
+
|
|
360
|
+
// boolean
|
|
361
|
+
active: 'boolean!',
|
|
362
|
+
|
|
363
|
+
// union type
|
|
364
|
+
contact: 'types:email|phone!', // email or phone, required
|
|
365
|
+
price2: 'types:number:0-|string', // number or string
|
|
366
|
+
})
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Built-in formats
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
dsl({
|
|
373
|
+
email: 'email!', // email address
|
|
374
|
+
website: 'url!', // URL
|
|
375
|
+
birthday: 'date!', // YYYY-MM-DD
|
|
376
|
+
createdAt: 'datetime!', // ISO 8601
|
|
377
|
+
userId: 'uuid!', // UUID
|
|
378
|
+
phone: 'phone:cn!', // Chinese mobile number
|
|
379
|
+
idCard: 'idCard:cn!', // Chinese national ID
|
|
380
|
+
slug: 'slug:3-100!', // URL-friendly string
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Fluent chain API (recommended for TypeScript)
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { dsl } from 'schema-dsl';
|
|
388
|
+
|
|
389
|
+
const schema = dsl({
|
|
390
|
+
username: dsl('string:3-32!')
|
|
391
|
+
.username()
|
|
392
|
+
.label('username')
|
|
393
|
+
.messages({ required: 'Username is required' }),
|
|
394
|
+
|
|
395
|
+
email: dsl('email!').label('email address'),
|
|
396
|
+
|
|
397
|
+
phone: dsl('string:11!')
|
|
398
|
+
.pattern(/^1[3-9]\d{9}$/)
|
|
399
|
+
.label('phone number'),
|
|
400
|
+
});
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Conditional validation
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// dsl.match — route to different rules based on a field value
|
|
407
|
+
const contactSchema = dsl({
|
|
408
|
+
type: 'email|phone|wechat',
|
|
409
|
+
contact: dsl.match('type', {
|
|
410
|
+
email: 'email!',
|
|
411
|
+
phone: 'string:11!',
|
|
412
|
+
wechat: 'string:6-20!',
|
|
413
|
+
})
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// dsl.if — simple conditional branch
|
|
417
|
+
const orderSchema = dsl({
|
|
418
|
+
isVip: 'boolean!',
|
|
419
|
+
discount: dsl.if('isVip', 'number:10-50!', 'number:0-10')
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// dsl.if chain assertion
|
|
423
|
+
dsl.if(d => !d.account)
|
|
424
|
+
.message('Account not found')
|
|
425
|
+
.and(d => d.account.balance < amount)
|
|
426
|
+
.message('Insufficient balance')
|
|
427
|
+
.assert(data);
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 🌍 Internationalization
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
import { dsl, validate, Locale, I18nError } from 'schema-dsl';
|
|
436
|
+
|
|
437
|
+
// built-in locales: zh-CN / en-US / ja-JP / es-ES / fr-FR (auto-loaded, no configuration needed)
|
|
438
|
+
const result = validate(schema, data, { locale: 'en-US' });
|
|
439
|
+
// error messages automatically use the specified locale
|
|
440
|
+
|
|
441
|
+
// register a custom locale
|
|
442
|
+
Locale.addLocale('zh-CN', {
|
|
443
|
+
'user.notFound': 'User not found',
|
|
444
|
+
'user.forbidden': { code: 40003, message: 'Access forbidden' },
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// throw i18n business errors
|
|
448
|
+
I18nError.assert(user, 'user.notFound'); // auto-throw when user is falsy
|
|
449
|
+
I18nError.throw('user.forbidden', {}, 403); // throw directly
|
|
450
|
+
I18nError.assert(ok, 'user.notFound', {}, 404, locale); // specify locale at runtime
|
|
451
|
+
|
|
452
|
+
// errors carry a numeric code; frontend can branch on it
|
|
453
|
+
try {
|
|
454
|
+
await api.getUser(id);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
switch (error.code) {
|
|
457
|
+
case 40003: showForbiddenPage(); break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## 🔌 Plugin System
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
import { PluginManager, Validator, dsl } from 'schema-dsl';
|
|
468
|
+
|
|
469
|
+
const pluginManager = new PluginManager();
|
|
470
|
+
|
|
471
|
+
// register a custom format plugin (must provide an install function)
|
|
472
|
+
pluginManager.register({
|
|
473
|
+
name: 'extra-formats',
|
|
474
|
+
install(core) {
|
|
475
|
+
const validator = core as Validator;
|
|
476
|
+
// register custom formats on the Validator instance via addFormat
|
|
477
|
+
validator.addFormat('hex-color', {
|
|
478
|
+
validate: (v: string) => /^#[0-9A-F]{6}$/i.test(v)
|
|
479
|
+
});
|
|
480
|
+
validator.addFormat('mac-address', {
|
|
481
|
+
validate: (v: string) => /^([0-9A-F]{2}:){5}[0-9A-F]{2}$/i.test(v)
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// create a Validator and install plugins
|
|
487
|
+
const validator = new Validator();
|
|
488
|
+
pluginManager.install(validator);
|
|
489
|
+
|
|
490
|
+
// use the custom formats in a schema
|
|
491
|
+
const schema = dsl({ color: 'hex-color!', mac: 'mac-address' });
|
|
492
|
+
const result = validator.validate(schema, { color: '#FF5733', mac: '00:1A:2B:3C:4D:5E' });
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## 🔧 Core API Reference
|
|
498
|
+
|
|
499
|
+
| API | Purpose | Returns | Docs |
|
|
500
|
+
|-----|---------|---------|------|
|
|
501
|
+
| `dsl(schema)` | Create a schema | Schema object | [DSL Syntax](https://vextjs.github.io/schema-dsl/dsl-syntax) |
|
|
502
|
+
| `validate(schema, data)` | Synchronous validation | `{ valid, errors, data }` | [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) |
|
|
503
|
+
| `validateAsync(schema, data)` | Asynchronous validation | Promise (throws on failure) | [Async Validation](https://vextjs.github.io/schema-dsl/validate-async) |
|
|
504
|
+
| `SchemaUtils.pick()` | Select fields | New schema | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
505
|
+
| `SchemaUtils.omit()` | Exclude fields | New schema | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
506
|
+
| `SchemaUtils.partial()` | Make all fields optional | New schema | [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils) |
|
|
507
|
+
| `dsl.if(condition)` | Conditional validation | ConditionalBuilder | [Conditional API](https://vextjs.github.io/schema-dsl/conditional-api) |
|
|
508
|
+
| `dsl.match(field, map)` | Branch validation | ConditionalBuilder | [Conditional API](https://vextjs.github.io/schema-dsl/conditional-api) |
|
|
509
|
+
| `I18nError.throw()` | Throw an i18n error | never | [Error Handling](https://vextjs.github.io/schema-dsl/error-handling) |
|
|
510
|
+
| `I18nError.assert()` | Assert then throw | void | [Error Handling](https://vextjs.github.io/schema-dsl/error-handling) |
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## 📝 TypeScript Usage
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { dsl, validateAsync, ValidationError } from 'schema-dsl';
|
|
518
|
+
|
|
519
|
+
// ✅ wrap strings with dsl() in TypeScript for full type inference
|
|
520
|
+
const userSchema = dsl({
|
|
521
|
+
username: dsl('string:3-32!').label('username'),
|
|
522
|
+
email: dsl('email!').label('email'),
|
|
523
|
+
age: dsl('number:18-100').label('age')
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
try {
|
|
527
|
+
const validData = await validateAsync(userSchema, payload);
|
|
528
|
+
// validData has full type inference
|
|
529
|
+
} catch (error) {
|
|
530
|
+
if (error instanceof ValidationError) {
|
|
531
|
+
error.errors.forEach(e => console.log(`${e.path}: ${e.message}`));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
> **Note**: In TypeScript projects, wrap strings with `dsl('...')` to get type inference. In JavaScript projects you can pass strings directly.
|
|
537
|
+
> See the [TypeScript Guide](https://vextjs.github.io/schema-dsl/typescript-guide) for details.
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## 🛠️ Development
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
npm run build # compile TypeScript
|
|
545
|
+
npm run test # run tests
|
|
546
|
+
npm run typecheck # type check
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Local documentation preview:
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
cd website
|
|
553
|
+
npm run dev
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## 🤝 Contributing
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
git clone https://github.com/vextjs/schema-dsl.git
|
|
562
|
+
cd schema-dsl
|
|
563
|
+
npm install
|
|
564
|
+
npm test
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## 🔗 Links
|
|
572
|
+
|
|
573
|
+
### 📖 Core documentation
|
|
574
|
+
- [Quick Start](https://vextjs.github.io/schema-dsl/quick-start) — up and running in 5 minutes
|
|
575
|
+
- [DSL Syntax Guide](https://vextjs.github.io/schema-dsl/dsl-syntax) — complete syntax reference
|
|
576
|
+
- [Validation Guide](https://vextjs.github.io/schema-dsl/validation-guide) — advanced validation techniques
|
|
577
|
+
- [API Reference](https://vextjs.github.io/schema-dsl/api-reference) — complete API docs
|
|
578
|
+
- [TypeScript Guide](https://vextjs.github.io/schema-dsl/typescript-guide) — required reading for TS users
|
|
579
|
+
- [Best Practices](https://vextjs.github.io/schema-dsl/best-practices) — avoid common pitfalls
|
|
580
|
+
- [Troubleshooting](https://vextjs.github.io/schema-dsl/troubleshooting) — diagnosing issues
|
|
581
|
+
|
|
582
|
+
### 🎯 Feature documentation
|
|
583
|
+
- [SchemaUtils](https://vextjs.github.io/schema-dsl/schema-utils)
|
|
584
|
+
- [Conditional Validation API](https://vextjs.github.io/schema-dsl/conditional-api)
|
|
585
|
+
- [Async Validation](https://vextjs.github.io/schema-dsl/validate-async)
|
|
586
|
+
- [Error Handling & i18n](https://vextjs.github.io/schema-dsl/error-handling)
|
|
587
|
+
- [Union Types](https://vextjs.github.io/schema-dsl/union-types)
|
|
588
|
+
- [Enum Types](https://vextjs.github.io/schema-dsl/enum)
|
|
589
|
+
|
|
590
|
+
### 🗄️ Export & integration
|
|
591
|
+
- [Export Guide](https://vextjs.github.io/schema-dsl/export-guide)
|
|
592
|
+
- [MongoDB Exporter](https://vextjs.github.io/schema-dsl/mongodb-exporter)
|
|
593
|
+
- [MySQL Exporter](https://vextjs.github.io/schema-dsl/mysql-exporter)
|
|
594
|
+
- [PostgreSQL Exporter](https://vextjs.github.io/schema-dsl/postgresql-exporter)
|
|
595
|
+
- [Markdown Exporter](https://vextjs.github.io/schema-dsl/markdown-exporter)
|
|
596
|
+
- [⚠️ Export Limitations](https://vextjs.github.io/schema-dsl/export-limitations)
|
|
597
|
+
|
|
598
|
+
### 💻 Examples
|
|
599
|
+
- [quick-start.ts](./examples/docs/quick-start.ts) — basic usage and registration form
|
|
600
|
+
- [validate-async.ts](./examples/docs/validate-async.ts) — async validation and `ValidationError` handling
|
|
601
|
+
- [export-guide.ts](./examples/docs/export-guide.ts) — database export overview
|
|
602
|
+
- [error-handling.ts](./examples/docs/error-handling.ts) — field errors and business error handling
|
|
603
|
+
- [plugin-system.ts](./examples/docs/plugin-system.ts) — plugin system and hooks
|
|
604
|
+
|
|
605
|
+
### 📝 Changelog & contributing
|
|
606
|
+
- [Changelog](./CHANGELOG.md)
|
|
607
|
+
- [Contributing Guide](./CONTRIBUTING.md)
|
|
608
|
+
- [Security Policy](./SECURITY.md)
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
## 📄 License
|
|
613
|
+
|
|
614
|
+
[MIT](./LICENSE)
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
<div align="center">
|
|
619
|
+
|
|
620
|
+
If this project is useful to you, please consider giving it a Star ⭐
|
|
621
|
+
|
|
622
|
+
Made with ❤️ by the schema-dsl team
|
|
623
|
+
|
|
624
|
+
</div>
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
|