@atscript/typescript 0.1.48 → 0.1.50
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/README.md +9 -6
- package/dist/index.cjs +4 -1480
- package/dist/index.mjs +2 -1454
- package/dist/plugin-D96fH7Va.mjs +1456 -0
- package/dist/plugin-cY7VLpmN.cjs +1491 -0
- package/dist/test-utils.cjs +27 -0
- package/dist/test-utils.d.ts +25 -0
- package/dist/test-utils.mjs +26 -0
- package/dist/utils.cjs +38 -22
- package/dist/utils.d.ts +10 -6
- package/dist/utils.mjs +38 -22
- package/package.json +20 -14
- package/scripts/setup-skills.js +0 -88
- package/skills/atscript-typescript/.gitkeep +0 -0
- package/skills/atscript-typescript/SKILL.md +0 -52
- package/skills/atscript-typescript/annotations.md +0 -259
- package/skills/atscript-typescript/codegen.md +0 -131
- package/skills/atscript-typescript/core.md +0 -166
- package/skills/atscript-typescript/runtime.md +0 -290
- package/skills/atscript-typescript/syntax.md +0 -252
- package/skills/atscript-typescript/utilities.md +0 -452
- package/skills/atscript-typescript/validation.md +0 -293
- /package/dist/{json-schema-Bu4xgpQn.cjs → json-schema-BgW_S2sP.cjs} +0 -0
- /package/dist/{json-schema-Bl8jkrCj.mjs → json-schema-DrJMwvm1.mjs} +0 -0
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
# Validation — @atscript/typescript
|
|
2
|
-
|
|
3
|
-
> Runtime data validation, type guards, error handling, and custom validator plugins.
|
|
4
|
-
|
|
5
|
-
## Basic Usage
|
|
6
|
-
|
|
7
|
-
Every generated `.as` interface/type has a `.validator()` factory:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { User } from './models/user.as'
|
|
11
|
-
|
|
12
|
-
// Create a validator
|
|
13
|
-
const validator = User.validator()
|
|
14
|
-
|
|
15
|
-
// Validate — throws ValidatorError on failure
|
|
16
|
-
validator.validate(data)
|
|
17
|
-
|
|
18
|
-
// Safe mode — returns boolean, no throw
|
|
19
|
-
if (validator.validate(data, true)) {
|
|
20
|
-
// data is narrowed to User (type guard)
|
|
21
|
-
data.name // TypeScript knows this exists
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## The `Validator` Class
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { Validator } from '@atscript/typescript/utils'
|
|
29
|
-
|
|
30
|
-
// Create from any annotated type
|
|
31
|
-
const validator = new Validator(someAnnotatedType, {
|
|
32
|
-
// Options (all optional):
|
|
33
|
-
partial: false, // allow missing required props
|
|
34
|
-
unknownProps: 'error', // 'error' | 'strip' | 'ignore'
|
|
35
|
-
errorLimit: 10, // max errors before stopping
|
|
36
|
-
plugins: [], // custom validator plugins
|
|
37
|
-
skipList: new Set(), // property paths to skip
|
|
38
|
-
})
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### `validate(value, safe?, context?)`
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
// Throwing mode (default) — throws ValidatorError on failure
|
|
45
|
-
validator.validate(data)
|
|
46
|
-
|
|
47
|
-
// Safe mode — returns false instead of throwing
|
|
48
|
-
const isValid = validator.validate(data, true)
|
|
49
|
-
|
|
50
|
-
// With external context — passed to plugins
|
|
51
|
-
validator.validate(data, true, { userId: '123' })
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
The `validate` method is a **TypeScript type guard** — when it returns `true`, the value is narrowed to the interface's data type.
|
|
55
|
-
|
|
56
|
-
## Validator Options
|
|
57
|
-
|
|
58
|
-
### `partial` — Allow Missing Properties
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
// Top-level properties only
|
|
62
|
-
User.validator({ partial: true }).validate(data, true)
|
|
63
|
-
|
|
64
|
-
// All levels (deep partial)
|
|
65
|
-
User.validator({ partial: 'deep' }).validate(data, true)
|
|
66
|
-
|
|
67
|
-
// Custom function — decide per object type
|
|
68
|
-
User.validator({
|
|
69
|
-
partial: (objectType, path) => {
|
|
70
|
-
return path === '' // only root object is partial
|
|
71
|
-
},
|
|
72
|
-
}).validate(data, true)
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### `unknownProps` — Handle Extra Properties
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
// Error on unknown properties (default)
|
|
79
|
-
User.validator({ unknownProps: 'error' }).validate(data, true)
|
|
80
|
-
|
|
81
|
-
// Silently remove unknown properties from the value
|
|
82
|
-
User.validator({ unknownProps: 'strip' }).validate(data, true)
|
|
83
|
-
|
|
84
|
-
// Ignore unknown properties
|
|
85
|
-
User.validator({ unknownProps: 'ignore' }).validate(data, true)
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**Note**: `'strip'` mutates the input object — it deletes unknown keys.
|
|
89
|
-
|
|
90
|
-
### `skipList` — Skip Specific Paths
|
|
91
|
-
|
|
92
|
-
```ts
|
|
93
|
-
User.validator({
|
|
94
|
-
skipList: new Set(['password', 'address.zip']),
|
|
95
|
-
}).validate(data, true)
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### `replace` — Substitute Type at Runtime
|
|
99
|
-
|
|
100
|
-
```ts
|
|
101
|
-
User.validator({
|
|
102
|
-
replace: (type, path) => {
|
|
103
|
-
if (path === 'status') return customStatusType
|
|
104
|
-
return type
|
|
105
|
-
},
|
|
106
|
-
}).validate(data, true)
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Error Handling
|
|
110
|
-
|
|
111
|
-
### `ValidatorError`
|
|
112
|
-
|
|
113
|
-
When `validate()` throws (non-safe mode), it throws a `ValidatorError`:
|
|
114
|
-
|
|
115
|
-
```ts
|
|
116
|
-
import { ValidatorError } from '@atscript/typescript/utils'
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
validator.validate(data)
|
|
120
|
-
} catch (e) {
|
|
121
|
-
if (e instanceof ValidatorError) {
|
|
122
|
-
// e.message — first error message (with path prefix)
|
|
123
|
-
// e.errors — full structured error array
|
|
124
|
-
console.log(e.errors)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Error Structure
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
interface TError {
|
|
133
|
-
path: string // dot-separated path, e.g. "address.city"
|
|
134
|
-
message: string // human-readable error message
|
|
135
|
-
details?: TError[] // nested errors (for unions — shows why each branch failed)
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### Reading Errors in Safe Mode
|
|
140
|
-
|
|
141
|
-
```ts
|
|
142
|
-
const validator = User.validator()
|
|
143
|
-
if (!validator.validate(data, true)) {
|
|
144
|
-
// Errors are on the validator instance
|
|
145
|
-
for (const error of validator.errors) {
|
|
146
|
-
console.log(`${error.path}: ${error.message}`)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Error Examples
|
|
152
|
-
|
|
153
|
-
```ts
|
|
154
|
-
// Missing required property
|
|
155
|
-
{ path: 'name', message: 'Expected string, got undefined' }
|
|
156
|
-
|
|
157
|
-
// Wrong type
|
|
158
|
-
{ path: 'age', message: 'Expected number, got string' }
|
|
159
|
-
|
|
160
|
-
// Pattern validation
|
|
161
|
-
{ path: 'email', message: 'Value is expected to match pattern "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"' }
|
|
162
|
-
|
|
163
|
-
// Custom annotation message
|
|
164
|
-
{ path: 'name', message: 'Name is required' } // from @meta.required "Name is required"
|
|
165
|
-
|
|
166
|
-
// Unknown property
|
|
167
|
-
{ path: 'foo', message: 'Unexpected property' }
|
|
168
|
-
|
|
169
|
-
// Union — shows why each branch failed
|
|
170
|
-
{
|
|
171
|
-
path: 'data',
|
|
172
|
-
message: 'Value does not match any of the allowed types: [string(0)], [number(1)]',
|
|
173
|
-
details: [
|
|
174
|
-
{ path: 'data', message: 'Expected string, got boolean' },
|
|
175
|
-
{ path: 'data', message: 'Expected number, got boolean' },
|
|
176
|
-
]
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Array validation
|
|
180
|
-
{ path: '2.name', message: 'Expected string, got number' } // 3rd element's name is wrong
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### Error Limit
|
|
184
|
-
|
|
185
|
-
By default, the validator stops collecting errors after 10. Customize:
|
|
186
|
-
|
|
187
|
-
```ts
|
|
188
|
-
User.validator({ errorLimit: 50 }).validate(data, true)
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## What Gets Validated
|
|
192
|
-
|
|
193
|
-
| Type Kind | Validation |
|
|
194
|
-
| -------------- | --------------------------------------------------------------------------------------------- |
|
|
195
|
-
| `string` | Type check + `@meta.required` (non-empty) + `@expect.minLength/maxLength` + `@expect.pattern` |
|
|
196
|
-
| `number` | Type check + `@expect.int` + `@expect.min/max` |
|
|
197
|
-
| `boolean` | Type check + `@meta.required` (must be true) |
|
|
198
|
-
| `null` | Exact `null` check |
|
|
199
|
-
| `undefined` | Exact `undefined` check |
|
|
200
|
-
| `any` | Always passes |
|
|
201
|
-
| `never` | Always fails |
|
|
202
|
-
| `phantom` | Always passes (skipped) |
|
|
203
|
-
| `object` | Recursively validates all props, handles unknown props, pattern props |
|
|
204
|
-
| `array` | Type check + `@expect.minLength/maxLength` + recursively validates each element |
|
|
205
|
-
| `union` | At least one branch must pass |
|
|
206
|
-
| `intersection` | All branches must pass |
|
|
207
|
-
| `tuple` | Array length must match + each element validated against its position |
|
|
208
|
-
| `literal` | Exact value match |
|
|
209
|
-
| `optional` | `undefined` is accepted; if value is present, validated against inner type |
|
|
210
|
-
|
|
211
|
-
## Custom Validator Plugins
|
|
212
|
-
|
|
213
|
-
Plugins intercept validation at every node in the type tree. They can accept, reject, or defer to default validation.
|
|
214
|
-
|
|
215
|
-
### Plugin Signature
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
|
-
type TValidatorPlugin = (
|
|
219
|
-
ctx: TValidatorPluginContext,
|
|
220
|
-
def: TAtscriptAnnotatedType,
|
|
221
|
-
value: any
|
|
222
|
-
) => boolean | undefined
|
|
223
|
-
// ↑ true = accept, false = reject, undefined = fall through to default
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Plugin Context
|
|
227
|
-
|
|
228
|
-
```ts
|
|
229
|
-
interface TValidatorPluginContext {
|
|
230
|
-
opts: TValidatorOptions // current validator options
|
|
231
|
-
validateAnnotatedType(def, value) // call default validation for a specific type
|
|
232
|
-
error(message, path?, details?) // report an error
|
|
233
|
-
path: string // current dot-separated path
|
|
234
|
-
context: unknown // external context from validate(data, safe, context)
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Plugin Example — Custom Date Validation
|
|
239
|
-
|
|
240
|
-
```ts
|
|
241
|
-
const datePlugin: TValidatorPlugin = (ctx, def, value) => {
|
|
242
|
-
// Only intercept string types tagged as dates
|
|
243
|
-
if (def.type.kind === '' && def.type.tags.has('date')) {
|
|
244
|
-
if (typeof value !== 'string') {
|
|
245
|
-
ctx.error('Expected date string')
|
|
246
|
-
return false
|
|
247
|
-
}
|
|
248
|
-
const parsed = Date.parse(value)
|
|
249
|
-
if (isNaN(parsed)) {
|
|
250
|
-
ctx.error(`Invalid date: "${value}"`)
|
|
251
|
-
return false
|
|
252
|
-
}
|
|
253
|
-
return true
|
|
254
|
-
}
|
|
255
|
-
// Return undefined to fall through to default validation
|
|
256
|
-
return undefined
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
User.validator({ plugins: [datePlugin] }).validate(data, true)
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### Plugin Return Values
|
|
263
|
-
|
|
264
|
-
| Return | Meaning |
|
|
265
|
-
| ----------- | ----------------------------------------------------------------------------------- |
|
|
266
|
-
| `true` | Value is accepted — skip all further validation for this node |
|
|
267
|
-
| `false` | Value is rejected — error should be reported via `ctx.error()` before returning |
|
|
268
|
-
| `undefined` | Plugin doesn't handle this type — fall through to next plugin or default validation |
|
|
269
|
-
|
|
270
|
-
### Plugin Example — Coerce String to Number
|
|
271
|
-
|
|
272
|
-
```ts
|
|
273
|
-
const coercePlugin: TValidatorPlugin = (ctx, def, value) => {
|
|
274
|
-
if (def.type.kind === '' && def.type.designType === 'number' && typeof value === 'string') {
|
|
275
|
-
const num = Number(value)
|
|
276
|
-
if (!isNaN(num)) {
|
|
277
|
-
// Validate the coerced value against the full type (respects @expect.min etc.)
|
|
278
|
-
return ctx.validateAnnotatedType(def, num)
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return undefined
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Multiple Plugins
|
|
286
|
-
|
|
287
|
-
Plugins run in order. The first plugin to return `true` or `false` wins — subsequent plugins and default validation are skipped for that node:
|
|
288
|
-
|
|
289
|
-
```ts
|
|
290
|
-
User.validator({
|
|
291
|
-
plugins: [authPlugin, datePlugin, coercePlugin],
|
|
292
|
-
}).validate(data, true)
|
|
293
|
-
```
|
|
File without changes
|
|
File without changes
|