@atscript/typescript 0.1.26 → 0.1.28
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 +50 -0
- package/dist/cli.cjs +4 -4
- package/dist/index.cjs +7 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +5 -9
- package/dist/utils.cjs +12 -1
- package/dist/utils.d.ts +4 -2
- package/dist/utils.mjs +12 -1
- package/package.json +5 -5
- package/scripts/setup-skills.js +23 -13
- package/skills/atscript-typescript/SKILL.md +23 -15
- package/skills/atscript-typescript/annotations.md +78 -60
- package/skills/atscript-typescript/codegen.md +14 -10
- package/skills/atscript-typescript/core.md +23 -21
- package/skills/atscript-typescript/runtime.md +66 -54
- package/skills/atscript-typescript/syntax.md +31 -31
- package/skills/atscript-typescript/utilities.md +94 -67
- package/skills/atscript-typescript/validation.md +39 -39
|
@@ -9,17 +9,25 @@ All utilities are exported from `@atscript/typescript/utils`:
|
|
|
9
9
|
```ts
|
|
10
10
|
import {
|
|
11
11
|
// Type construction
|
|
12
|
-
defineAnnotatedType,
|
|
12
|
+
defineAnnotatedType,
|
|
13
|
+
annotate,
|
|
13
14
|
// Type checking
|
|
14
|
-
isAnnotatedType,
|
|
15
|
+
isAnnotatedType,
|
|
16
|
+
isAnnotatedTypeOfPrimitive,
|
|
17
|
+
isPhantomType,
|
|
15
18
|
// Type traversal
|
|
16
19
|
forAnnotatedType,
|
|
17
20
|
// Validation
|
|
18
|
-
Validator,
|
|
21
|
+
Validator,
|
|
22
|
+
ValidatorError,
|
|
19
23
|
// JSON Schema
|
|
20
|
-
buildJsonSchema,
|
|
24
|
+
buildJsonSchema,
|
|
25
|
+
fromJsonSchema,
|
|
26
|
+
mergeJsonSchemas,
|
|
21
27
|
// Serialization
|
|
22
|
-
serializeAnnotatedType,
|
|
28
|
+
serializeAnnotatedType,
|
|
29
|
+
deserializeAnnotatedType,
|
|
30
|
+
SERIALIZE_VERSION,
|
|
23
31
|
// Flattening
|
|
24
32
|
flattenAnnotatedType,
|
|
25
33
|
// Data creation
|
|
@@ -41,13 +49,27 @@ Dispatches over `TAtscriptAnnotatedType` by its `type.kind`, providing type-narr
|
|
|
41
49
|
import { forAnnotatedType } from '@atscript/typescript/utils'
|
|
42
50
|
|
|
43
51
|
const description = forAnnotatedType(someType, {
|
|
44
|
-
final(d)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
final(d) {
|
|
53
|
+
return `${d.type.designType}`
|
|
54
|
+
},
|
|
55
|
+
object(d) {
|
|
56
|
+
return `object(${d.type.props.size} props)`
|
|
57
|
+
},
|
|
58
|
+
array(d) {
|
|
59
|
+
return `array`
|
|
60
|
+
},
|
|
61
|
+
union(d) {
|
|
62
|
+
return `union(${d.type.items.length})`
|
|
63
|
+
},
|
|
64
|
+
intersection(d) {
|
|
65
|
+
return `intersection(${d.type.items.length})`
|
|
66
|
+
},
|
|
67
|
+
tuple(d) {
|
|
68
|
+
return `[${d.type.items.length}]`
|
|
69
|
+
},
|
|
70
|
+
phantom(d) {
|
|
71
|
+
return `phantom`
|
|
72
|
+
}, // optional — without it, phantoms go to final
|
|
51
73
|
})
|
|
52
74
|
```
|
|
53
75
|
|
|
@@ -88,6 +110,7 @@ const schema = buildJsonSchema(CatOrDog)
|
|
|
88
110
|
```
|
|
89
111
|
|
|
90
112
|
Key behaviors:
|
|
113
|
+
|
|
91
114
|
- Only **named object types** (with `id`) are extracted to `$defs`. Primitives, unions, arrays stay inline.
|
|
92
115
|
- The **root type** is never extracted — it IS the schema.
|
|
93
116
|
- Same `id` referenced multiple times → one `$defs` entry, all occurrences become `$ref`.
|
|
@@ -96,23 +119,23 @@ Key behaviors:
|
|
|
96
119
|
|
|
97
120
|
### Metadata → JSON Schema Mapping
|
|
98
121
|
|
|
99
|
-
| Annotation
|
|
100
|
-
|
|
101
|
-
| `@expect.minLength` on string | `minLength`
|
|
102
|
-
| `@expect.maxLength` on string | `maxLength`
|
|
103
|
-
| `@expect.minLength` on array
|
|
104
|
-
| `@expect.maxLength` on array
|
|
105
|
-
| `@expect.min`
|
|
106
|
-
| `@expect.max`
|
|
107
|
-
| `@expect.int`
|
|
108
|
-
| `@expect.pattern` (single)
|
|
109
|
-
| `@expect.pattern` (multiple)
|
|
110
|
-
| `@meta.required` on string
|
|
111
|
-
| optional property
|
|
112
|
-
| union
|
|
113
|
-
| intersection
|
|
114
|
-
| tuple
|
|
115
|
-
| phantom
|
|
122
|
+
| Annotation | JSON Schema |
|
|
123
|
+
| ----------------------------- | --------------------------------------------------------------- |
|
|
124
|
+
| `@expect.minLength` on string | `minLength` |
|
|
125
|
+
| `@expect.maxLength` on string | `maxLength` |
|
|
126
|
+
| `@expect.minLength` on array | `minItems` |
|
|
127
|
+
| `@expect.maxLength` on array | `maxItems` |
|
|
128
|
+
| `@expect.min` | `minimum` |
|
|
129
|
+
| `@expect.max` | `maximum` |
|
|
130
|
+
| `@expect.int` | `type: 'integer'` (instead of `'number'`) |
|
|
131
|
+
| `@expect.pattern` (single) | `pattern` |
|
|
132
|
+
| `@expect.pattern` (multiple) | `allOf: [{ pattern }, ...]` |
|
|
133
|
+
| `@meta.required` on string | `minLength: 1` |
|
|
134
|
+
| optional property | not in `required` array |
|
|
135
|
+
| union | `anyOf` (or `oneOf` + `discriminator` for discriminated unions) |
|
|
136
|
+
| intersection | `allOf` |
|
|
137
|
+
| tuple | `items` as array |
|
|
138
|
+
| phantom | empty object `{}` (excluded) |
|
|
116
139
|
|
|
117
140
|
### Discriminated Unions
|
|
118
141
|
|
|
@@ -131,11 +154,11 @@ const type = fromJsonSchema({
|
|
|
131
154
|
name: { type: 'string', minLength: 1 },
|
|
132
155
|
age: { type: 'integer', minimum: 0 },
|
|
133
156
|
},
|
|
134
|
-
required: ['name', 'age']
|
|
157
|
+
required: ['name', 'age'],
|
|
135
158
|
})
|
|
136
159
|
|
|
137
160
|
// The resulting type has a working validator
|
|
138
|
-
type.validator().validate({ name: 'Alice', age: 30 })
|
|
161
|
+
type.validator().validate({ name: 'Alice', age: 30 }) // passes
|
|
139
162
|
```
|
|
140
163
|
|
|
141
164
|
Supports: `type`, `properties`, `required`, `items`, `anyOf`, `oneOf`, `allOf`, `enum`, `const`, `minLength`, `maxLength`, `minimum`, `maximum`, `pattern`, `minItems`, `maxItems`, `$ref`/`$defs`.
|
|
@@ -288,19 +311,19 @@ const custom = createDataFromAnnotatedType(User, {
|
|
|
288
311
|
mode: (prop, path) => {
|
|
289
312
|
if (path === 'name') return 'John Doe'
|
|
290
313
|
if (path === 'age') return 25
|
|
291
|
-
return undefined
|
|
292
|
-
}
|
|
314
|
+
return undefined // fall through to structural default
|
|
315
|
+
},
|
|
293
316
|
})
|
|
294
317
|
```
|
|
295
318
|
|
|
296
319
|
### Modes
|
|
297
320
|
|
|
298
|
-
| Mode
|
|
299
|
-
|
|
300
|
-
| `'empty'` (default) | Structural defaults: `''`, `0`, `false`, `[]`, `{}`. Optional props omitted
|
|
301
|
-
| `'default'`
|
|
302
|
-
| `'example'`
|
|
303
|
-
| `function`
|
|
321
|
+
| Mode | Behavior |
|
|
322
|
+
| ------------------- | -------------------------------------------------------------------------------------------- |
|
|
323
|
+
| `'empty'` (default) | Structural defaults: `''`, `0`, `false`, `[]`, `{}`. Optional props omitted |
|
|
324
|
+
| `'default'` | Uses `@meta.default` annotations. Optional props only included if annotated |
|
|
325
|
+
| `'example'` | Uses `@meta.example` annotations. Optional props always included. Arrays get one sample item |
|
|
326
|
+
| `function` | Custom resolver per field. Return `undefined` to fall through |
|
|
304
327
|
|
|
305
328
|
### Behavior Notes
|
|
306
329
|
|
|
@@ -317,8 +340,8 @@ const custom = createDataFromAnnotatedType(User, {
|
|
|
317
340
|
import { isAnnotatedType } from '@atscript/typescript/utils'
|
|
318
341
|
|
|
319
342
|
if (isAnnotatedType(value)) {
|
|
320
|
-
value.metadata
|
|
321
|
-
value.type
|
|
343
|
+
value.metadata // safe
|
|
344
|
+
value.type // safe
|
|
322
345
|
}
|
|
323
346
|
```
|
|
324
347
|
|
|
@@ -329,10 +352,10 @@ Returns `true` for final types and for unions/intersections/tuples whose all mem
|
|
|
329
352
|
```ts
|
|
330
353
|
import { isAnnotatedTypeOfPrimitive } from '@atscript/typescript/utils'
|
|
331
354
|
|
|
332
|
-
isAnnotatedTypeOfPrimitive(stringType)
|
|
333
|
-
isAnnotatedTypeOfPrimitive(objectType)
|
|
334
|
-
isAnnotatedTypeOfPrimitive(unionOfStringAndNumber)
|
|
335
|
-
isAnnotatedTypeOfPrimitive(unionOfStringAndObject)
|
|
355
|
+
isAnnotatedTypeOfPrimitive(stringType) // true
|
|
356
|
+
isAnnotatedTypeOfPrimitive(objectType) // false
|
|
357
|
+
isAnnotatedTypeOfPrimitive(unionOfStringAndNumber) // true
|
|
358
|
+
isAnnotatedTypeOfPrimitive(unionOfStringAndObject) // false
|
|
336
359
|
```
|
|
337
360
|
|
|
338
361
|
## `isPhantomType(def)` — Check if Phantom
|
|
@@ -340,7 +363,7 @@ isAnnotatedTypeOfPrimitive(unionOfStringAndObject) // false
|
|
|
340
363
|
```ts
|
|
341
364
|
import { isPhantomType } from '@atscript/typescript/utils'
|
|
342
365
|
|
|
343
|
-
isPhantomType(someProperty)
|
|
366
|
+
isPhantomType(someProperty) // true if designType === 'phantom'
|
|
344
367
|
```
|
|
345
368
|
|
|
346
369
|
## `TAtscriptDataType<T>` — Extract DataType from Annotated Type
|
|
@@ -370,8 +393,12 @@ import type { TAtscriptAnnotatedType, TAtscriptDataType } from '@atscript/typesc
|
|
|
370
393
|
|
|
371
394
|
// Generic repository that infers its entity type
|
|
372
395
|
class Repository<T extends TAtscriptAnnotatedType> {
|
|
373
|
-
findOne(id: string): Promise<TAtscriptDataType<T>> {
|
|
374
|
-
|
|
396
|
+
findOne(id: string): Promise<TAtscriptDataType<T>> {
|
|
397
|
+
/* ... */
|
|
398
|
+
}
|
|
399
|
+
insertOne(data: TAtscriptDataType<T>): Promise<void> {
|
|
400
|
+
/* ... */
|
|
401
|
+
}
|
|
375
402
|
}
|
|
376
403
|
|
|
377
404
|
// Usage — DataType is automatically inferred
|
|
@@ -398,25 +425,25 @@ Key types you may need to import:
|
|
|
398
425
|
|
|
399
426
|
```ts
|
|
400
427
|
import type {
|
|
401
|
-
TAtscriptAnnotatedType,
|
|
428
|
+
TAtscriptAnnotatedType, // core annotated type
|
|
402
429
|
TAtscriptAnnotatedTypeConstructor, // annotated type that's also a class
|
|
403
|
-
TAtscriptTypeDef,
|
|
404
|
-
TAtscriptTypeFinal,
|
|
405
|
-
TAtscriptTypeObject,
|
|
406
|
-
TAtscriptTypeArray,
|
|
407
|
-
TAtscriptTypeComplex,
|
|
408
|
-
TMetadataMap,
|
|
409
|
-
TAnnotatedTypeHandle,
|
|
410
|
-
InferDataType,
|
|
411
|
-
TAtscriptDataType,
|
|
412
|
-
TValidatorOptions,
|
|
413
|
-
TValidatorPlugin,
|
|
414
|
-
TValidatorPluginContext,
|
|
415
|
-
TSerializedAnnotatedType,
|
|
416
|
-
TSerializeOptions,
|
|
417
|
-
TFlattenOptions,
|
|
418
|
-
TCreateDataOptions,
|
|
419
|
-
TValueResolver,
|
|
420
|
-
TJsonSchema,
|
|
430
|
+
TAtscriptTypeDef, // union of all type def shapes
|
|
431
|
+
TAtscriptTypeFinal, // primitive/literal type def
|
|
432
|
+
TAtscriptTypeObject, // object type def
|
|
433
|
+
TAtscriptTypeArray, // array type def
|
|
434
|
+
TAtscriptTypeComplex, // union/intersection/tuple type def
|
|
435
|
+
TMetadataMap, // typed metadata map
|
|
436
|
+
TAnnotatedTypeHandle, // fluent builder handle
|
|
437
|
+
InferDataType, // extract DataType from a type def's phantom generic
|
|
438
|
+
TAtscriptDataType, // extract DataType from TAtscriptAnnotatedType
|
|
439
|
+
TValidatorOptions, // validator config
|
|
440
|
+
TValidatorPlugin, // plugin function type
|
|
441
|
+
TValidatorPluginContext, // plugin context
|
|
442
|
+
TSerializedAnnotatedType, // serialized type (top-level)
|
|
443
|
+
TSerializeOptions, // serialization options
|
|
444
|
+
TFlattenOptions, // flatten options
|
|
445
|
+
TCreateDataOptions, // createData options
|
|
446
|
+
TValueResolver, // custom resolver for createData
|
|
447
|
+
TJsonSchema, // JSON Schema object
|
|
421
448
|
} from '@atscript/typescript/utils'
|
|
422
449
|
```
|
|
@@ -18,7 +18,7 @@ validator.validate(data)
|
|
|
18
18
|
// Safe mode — returns boolean, no throw
|
|
19
19
|
if (validator.validate(data, true)) {
|
|
20
20
|
// data is narrowed to User (type guard)
|
|
21
|
-
data.name
|
|
21
|
+
data.name // TypeScript knows this exists
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
@@ -30,11 +30,11 @@ import { Validator } from '@atscript/typescript/utils'
|
|
|
30
30
|
// Create from any annotated type
|
|
31
31
|
const validator = new Validator(someAnnotatedType, {
|
|
32
32
|
// Options (all optional):
|
|
33
|
-
partial: false,
|
|
34
|
-
unknownProps: 'error',
|
|
35
|
-
errorLimit: 10,
|
|
36
|
-
plugins: [],
|
|
37
|
-
skipList: new Set(),
|
|
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
38
|
})
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -68,7 +68,7 @@ User.validator({ partial: 'deep' }).validate(data, true)
|
|
|
68
68
|
User.validator({
|
|
69
69
|
partial: (objectType, path) => {
|
|
70
70
|
return path === '' // only root object is partial
|
|
71
|
-
}
|
|
71
|
+
},
|
|
72
72
|
}).validate(data, true)
|
|
73
73
|
```
|
|
74
74
|
|
|
@@ -91,7 +91,7 @@ User.validator({ unknownProps: 'ignore' }).validate(data, true)
|
|
|
91
91
|
|
|
92
92
|
```ts
|
|
93
93
|
User.validator({
|
|
94
|
-
skipList: new Set(['password', 'address.zip'])
|
|
94
|
+
skipList: new Set(['password', 'address.zip']),
|
|
95
95
|
}).validate(data, true)
|
|
96
96
|
```
|
|
97
97
|
|
|
@@ -102,7 +102,7 @@ User.validator({
|
|
|
102
102
|
replace: (type, path) => {
|
|
103
103
|
if (path === 'status') return customStatusType
|
|
104
104
|
return type
|
|
105
|
-
}
|
|
105
|
+
},
|
|
106
106
|
}).validate(data, true)
|
|
107
107
|
```
|
|
108
108
|
|
|
@@ -130,9 +130,9 @@ try {
|
|
|
130
130
|
|
|
131
131
|
```ts
|
|
132
132
|
interface TError {
|
|
133
|
-
path: string
|
|
134
|
-
message: string
|
|
135
|
-
details?: 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
136
|
}
|
|
137
137
|
```
|
|
138
138
|
|
|
@@ -190,23 +190,23 @@ User.validator({ errorLimit: 50 }).validate(data, true)
|
|
|
190
190
|
|
|
191
191
|
## What Gets Validated
|
|
192
192
|
|
|
193
|
-
| Type Kind
|
|
194
|
-
|
|
195
|
-
| `string`
|
|
196
|
-
| `number`
|
|
197
|
-
| `boolean`
|
|
198
|
-
| `null`
|
|
199
|
-
| `undefined`
|
|
200
|
-
| `any`
|
|
201
|
-
| `never`
|
|
202
|
-
| `phantom`
|
|
203
|
-
| `object`
|
|
204
|
-
| `array`
|
|
205
|
-
| `union`
|
|
206
|
-
| `intersection` | All branches must pass
|
|
207
|
-
| `tuple`
|
|
208
|
-
| `literal`
|
|
209
|
-
| `optional`
|
|
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
210
|
|
|
211
211
|
## Custom Validator Plugins
|
|
212
212
|
|
|
@@ -227,11 +227,11 @@ type TValidatorPlugin = (
|
|
|
227
227
|
|
|
228
228
|
```ts
|
|
229
229
|
interface TValidatorPluginContext {
|
|
230
|
-
opts: TValidatorOptions
|
|
231
|
-
validateAnnotatedType(def, value)
|
|
232
|
-
error(message, path?, details?)
|
|
233
|
-
path: string
|
|
234
|
-
context: unknown
|
|
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
235
|
}
|
|
236
236
|
```
|
|
237
237
|
|
|
@@ -261,10 +261,10 @@ User.validator({ plugins: [datePlugin] }).validate(data, true)
|
|
|
261
261
|
|
|
262
262
|
### Plugin Return Values
|
|
263
263
|
|
|
264
|
-
| Return
|
|
265
|
-
|
|
266
|
-
| `true`
|
|
267
|
-
| `false`
|
|
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
268
|
| `undefined` | Plugin doesn't handle this type — fall through to next plugin or default validation |
|
|
269
269
|
|
|
270
270
|
### Plugin Example — Coerce String to Number
|
|
@@ -288,6 +288,6 @@ Plugins run in order. The first plugin to return `true` or `false` wins — subs
|
|
|
288
288
|
|
|
289
289
|
```ts
|
|
290
290
|
User.validator({
|
|
291
|
-
plugins: [authPlugin, datePlugin, coercePlugin]
|
|
291
|
+
plugins: [authPlugin, datePlugin, coercePlugin],
|
|
292
292
|
}).validate(data, true)
|
|
293
293
|
```
|