@atscript/typescript 0.1.25 → 0.1.27
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 +140 -46
- package/dist/index.cjs +143 -51
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +141 -51
- package/dist/utils.cjs +163 -39
- package/dist/utils.d.ts +24 -2
- package/dist/utils.mjs +162 -40
- package/package.json +5 -5
- package/scripts/setup-skills.js +23 -13
- package/skills/atscript-typescript/SKILL.md +22 -14
- package/skills/atscript-typescript/annotations.md +51 -50
- package/skills/atscript-typescript/codegen.md +15 -10
- package/skills/atscript-typescript/core.md +23 -21
- package/skills/atscript-typescript/runtime.md +66 -52
- package/skills/atscript-typescript/syntax.md +31 -31
- package/skills/atscript-typescript/utilities.md +137 -72
- package/skills/atscript-typescript/validation.md +39 -39
|
@@ -8,11 +8,12 @@ Every generated interface/type is a `TAtscriptAnnotatedType` — the core runtim
|
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
interface TAtscriptAnnotatedType<T extends TAtscriptTypeDef = TAtscriptTypeDef> {
|
|
11
|
-
__is_atscript_annotated_type: true
|
|
12
|
-
type: T
|
|
13
|
-
metadata: TMetadataMap<AtscriptMetadata>
|
|
14
|
-
optional?: boolean
|
|
15
|
-
|
|
11
|
+
__is_atscript_annotated_type: true // brand for type checking
|
|
12
|
+
type: T // the type definition (shape)
|
|
13
|
+
metadata: TMetadataMap<AtscriptMetadata> // annotation metadata
|
|
14
|
+
optional?: boolean // whether this type is optional
|
|
15
|
+
id?: string // stable type name (set by codegen or .id() builder)
|
|
16
|
+
validator(opts?): Validator // create a validator instance
|
|
16
17
|
}
|
|
17
18
|
```
|
|
18
19
|
|
|
@@ -37,7 +38,7 @@ The `type` field describes the shape. There are 5 kinds:
|
|
|
37
38
|
interface TAtscriptTypeFinal {
|
|
38
39
|
kind: ''
|
|
39
40
|
designType: 'string' | 'number' | 'boolean' | 'undefined' | 'null' | 'any' | 'never' | 'phantom'
|
|
40
|
-
value?: string | number | boolean
|
|
41
|
+
value?: string | number | boolean // for literal types
|
|
41
42
|
tags: Set<AtscriptPrimitiveTags>
|
|
42
43
|
}
|
|
43
44
|
```
|
|
@@ -47,8 +48,8 @@ interface TAtscriptTypeFinal {
|
|
|
47
48
|
```ts
|
|
48
49
|
interface TAtscriptTypeObject<K extends string = string> {
|
|
49
50
|
kind: 'object'
|
|
50
|
-
props: Map<K, TAtscriptAnnotatedType>
|
|
51
|
-
propsPatterns: Array<{ pattern: RegExp; def: TAtscriptAnnotatedType }>
|
|
51
|
+
props: Map<K, TAtscriptAnnotatedType> // named properties
|
|
52
|
+
propsPatterns: Array<{ pattern: RegExp; def: TAtscriptAnnotatedType }> // pattern properties
|
|
52
53
|
tags: Set<AtscriptPrimitiveTags>
|
|
53
54
|
}
|
|
54
55
|
```
|
|
@@ -58,7 +59,7 @@ interface TAtscriptTypeObject<K extends string = string> {
|
|
|
58
59
|
```ts
|
|
59
60
|
interface TAtscriptTypeArray {
|
|
60
61
|
kind: 'array'
|
|
61
|
-
of: TAtscriptAnnotatedType
|
|
62
|
+
of: TAtscriptAnnotatedType // element type
|
|
62
63
|
tags: Set<AtscriptPrimitiveTags>
|
|
63
64
|
}
|
|
64
65
|
```
|
|
@@ -81,12 +82,12 @@ The `metadata` field is a typed `Map<keyof AtscriptMetadata, value>`:
|
|
|
81
82
|
import { User } from './models/user.as'
|
|
82
83
|
|
|
83
84
|
// Read annotations
|
|
84
|
-
const label = User.metadata.get('meta.label')
|
|
85
|
-
const required = User.metadata.get('meta.required')
|
|
86
|
-
const minLen = User.metadata.get('expect.minLength')
|
|
85
|
+
const label = User.metadata.get('meta.label') // string | undefined
|
|
86
|
+
const required = User.metadata.get('meta.required') // { message?: string } | true | undefined
|
|
87
|
+
const minLen = User.metadata.get('expect.minLength') // { length: number; message?: string } | undefined
|
|
87
88
|
|
|
88
89
|
// Check if annotation exists
|
|
89
|
-
User.metadata.has('meta.sensitive')
|
|
90
|
+
User.metadata.has('meta.sensitive') // boolean
|
|
90
91
|
|
|
91
92
|
// Iterate all annotations
|
|
92
93
|
for (const [key, value] of User.metadata.entries()) {
|
|
@@ -103,11 +104,11 @@ Navigate into object properties via `type.props`:
|
|
|
103
104
|
const nameProp = User.type.props.get('name')!
|
|
104
105
|
|
|
105
106
|
// Read that property's metadata
|
|
106
|
-
nameProp.metadata.get('meta.label')
|
|
107
|
-
nameProp.metadata.get('meta.required')
|
|
107
|
+
nameProp.metadata.get('meta.label') // "Full Name"
|
|
108
|
+
nameProp.metadata.get('meta.required') // true or { message: "..." }
|
|
108
109
|
|
|
109
110
|
// Check if optional
|
|
110
|
-
nameProp.optional
|
|
111
|
+
nameProp.optional // boolean | undefined
|
|
111
112
|
```
|
|
112
113
|
|
|
113
114
|
### Nested Properties
|
|
@@ -147,15 +148,15 @@ The `annotate()` function handles array annotations correctly — if `asArray` i
|
|
|
147
148
|
```ts
|
|
148
149
|
function inspect(def: TAtscriptAnnotatedType) {
|
|
149
150
|
switch (def.type.kind) {
|
|
150
|
-
case '':
|
|
151
|
+
case '': // final/primitive
|
|
151
152
|
console.log('Primitive:', def.type.designType)
|
|
152
153
|
break
|
|
153
|
-
case 'object':
|
|
154
|
+
case 'object': // object with props
|
|
154
155
|
for (const [name, prop] of def.type.props) {
|
|
155
156
|
console.log(` ${name}:`, prop.type.kind || prop.type.designType)
|
|
156
157
|
}
|
|
157
158
|
break
|
|
158
|
-
case 'array':
|
|
159
|
+
case 'array': // array
|
|
159
160
|
console.log('Array of:', def.type.of.type.kind)
|
|
160
161
|
break
|
|
161
162
|
case 'union':
|
|
@@ -175,14 +176,28 @@ A type-safe dispatch helper that covers all `kind` values:
|
|
|
175
176
|
import { forAnnotatedType } from '@atscript/typescript/utils'
|
|
176
177
|
|
|
177
178
|
const result = forAnnotatedType(someType, {
|
|
178
|
-
final(d)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
final(d) {
|
|
180
|
+
return `primitive: ${d.type.designType}`
|
|
181
|
+
},
|
|
182
|
+
object(d) {
|
|
183
|
+
return `object with ${d.type.props.size} props`
|
|
184
|
+
},
|
|
185
|
+
array(d) {
|
|
186
|
+
return `array`
|
|
187
|
+
},
|
|
188
|
+
union(d) {
|
|
189
|
+
return `union of ${d.type.items.length}`
|
|
190
|
+
},
|
|
191
|
+
intersection(d) {
|
|
192
|
+
return `intersection of ${d.type.items.length}`
|
|
193
|
+
},
|
|
194
|
+
tuple(d) {
|
|
195
|
+
return `tuple of ${d.type.items.length}`
|
|
196
|
+
},
|
|
184
197
|
// Optional: handle phantom types separately from final
|
|
185
|
-
phantom(d)
|
|
198
|
+
phantom(d) {
|
|
199
|
+
return `phantom`
|
|
200
|
+
},
|
|
186
201
|
})
|
|
187
202
|
```
|
|
188
203
|
|
|
@@ -194,7 +209,7 @@ Each type definition has a `tags` Set containing primitive tags (e.g. `"string"`
|
|
|
194
209
|
|
|
195
210
|
```ts
|
|
196
211
|
const nameProp = User.type.props.get('name')!
|
|
197
|
-
nameProp.type.tags.has('string')
|
|
212
|
+
nameProp.type.tags.has('string') // true
|
|
198
213
|
```
|
|
199
214
|
|
|
200
215
|
Tags come from primitive definitions and their extensions. They're useful for categorizing types at runtime.
|
|
@@ -220,7 +235,7 @@ for (const [name, prop] of User.type.props) {
|
|
|
220
235
|
import { isAnnotatedTypeOfPrimitive } from '@atscript/typescript/utils'
|
|
221
236
|
|
|
222
237
|
// Returns true for final types and unions/intersections/tuples of all primitives
|
|
223
|
-
isAnnotatedTypeOfPrimitive(someType)
|
|
238
|
+
isAnnotatedTypeOfPrimitive(someType) // true if no objects or arrays
|
|
224
239
|
```
|
|
225
240
|
|
|
226
241
|
## Building Types at Runtime
|
|
@@ -237,40 +252,39 @@ const strType = defineAnnotatedType().designType('string').tags('string').$type
|
|
|
237
252
|
const userType = defineAnnotatedType('object')
|
|
238
253
|
.prop('name', defineAnnotatedType().designType('string').$type)
|
|
239
254
|
.prop('age', defineAnnotatedType().designType('number').$type)
|
|
240
|
-
.prop('email', defineAnnotatedType().optional().designType('string').$type)
|
|
241
|
-
.$type
|
|
255
|
+
.prop('email', defineAnnotatedType().optional().designType('string').$type).$type
|
|
242
256
|
|
|
243
257
|
// Array
|
|
244
|
-
const listType = defineAnnotatedType('array')
|
|
245
|
-
|
|
246
|
-
|
|
258
|
+
const listType = defineAnnotatedType('array').of(
|
|
259
|
+
defineAnnotatedType().designType('string').$type
|
|
260
|
+
).$type
|
|
247
261
|
|
|
248
262
|
// Union
|
|
249
263
|
const statusType = defineAnnotatedType('union')
|
|
250
264
|
.item(defineAnnotatedType().designType('string').value('active').$type)
|
|
251
|
-
.item(defineAnnotatedType().designType('string').value('inactive').$type)
|
|
252
|
-
.$type
|
|
265
|
+
.item(defineAnnotatedType().designType('string').value('inactive').$type).$type
|
|
253
266
|
|
|
254
267
|
// With metadata
|
|
255
|
-
const labeledType = defineAnnotatedType()
|
|
268
|
+
const labeledType = defineAnnotatedType()
|
|
269
|
+
.designType('string')
|
|
256
270
|
.annotate('meta.label', 'My Label')
|
|
257
|
-
.annotate('expect.minLength', { length: 3 })
|
|
258
|
-
.$type
|
|
271
|
+
.annotate('expect.minLength', { length: 3 }).$type
|
|
259
272
|
```
|
|
260
273
|
|
|
261
274
|
### `TAnnotatedTypeHandle` Fluent API
|
|
262
275
|
|
|
263
|
-
| Method
|
|
264
|
-
|
|
265
|
-
| `.designType(dt)`
|
|
266
|
-
| `.value(v)`
|
|
267
|
-
| `.tags(...tags)`
|
|
268
|
-
| `.prop(name, type)`
|
|
269
|
-
| `.propPattern(regex, type)`
|
|
270
|
-
| `.of(type)`
|
|
271
|
-
| `.item(type)`
|
|
272
|
-
| `.optional(flag?)`
|
|
273
|
-
| `.annotate(key, value, asArray?)` | Set metadata annotation
|
|
274
|
-
| `.copyMetadata(from, ignore?)`
|
|
275
|
-
| `.
|
|
276
|
-
|
|
|
276
|
+
| Method | Description |
|
|
277
|
+
| --------------------------------- | --------------------------------------------------------------------- |
|
|
278
|
+
| `.designType(dt)` | Set primitive design type |
|
|
279
|
+
| `.value(v)` | Set literal value |
|
|
280
|
+
| `.tags(...tags)` | Add primitive tags |
|
|
281
|
+
| `.prop(name, type)` | Add named property (object kind) |
|
|
282
|
+
| `.propPattern(regex, type)` | Add pattern property (object kind) |
|
|
283
|
+
| `.of(type)` | Set element type (array kind) |
|
|
284
|
+
| `.item(type)` | Add item (union/intersection/tuple kind) |
|
|
285
|
+
| `.optional(flag?)` | Mark as optional |
|
|
286
|
+
| `.annotate(key, value, asArray?)` | Set metadata annotation |
|
|
287
|
+
| `.copyMetadata(from, ignore?)` | Copy metadata from another type |
|
|
288
|
+
| `.id(name)` | Set a stable type name (used by `buildJsonSchema` for `$defs`/`$ref`) |
|
|
289
|
+
| `.refTo(type, chain?)` | Reference another annotated type's definition (carries `id`) |
|
|
290
|
+
| `.$type` | Get the final `TAtscriptAnnotatedType` |
|
|
@@ -120,15 +120,15 @@ annotate User {
|
|
|
120
120
|
|
|
121
121
|
### Built-in Primitives
|
|
122
122
|
|
|
123
|
-
| Primitive
|
|
124
|
-
|
|
125
|
-
| `string`
|
|
126
|
-
| `number`
|
|
127
|
-
| `boolean`
|
|
128
|
-
| `null`
|
|
129
|
-
| `void` / `undefined` | No value
|
|
130
|
-
| `never`
|
|
131
|
-
| `phantom`
|
|
123
|
+
| Primitive | Description |
|
|
124
|
+
| -------------------- | --------------------------------------------- |
|
|
125
|
+
| `string` | Text data |
|
|
126
|
+
| `number` | Numeric data |
|
|
127
|
+
| `boolean` | True/false |
|
|
128
|
+
| `null` | Null value |
|
|
129
|
+
| `void` / `undefined` | No value |
|
|
130
|
+
| `never` | Impossible type |
|
|
131
|
+
| `phantom` | Metadata-only type (no runtime/schema impact) |
|
|
132
132
|
|
|
133
133
|
### Primitive Extensions (Subtypes)
|
|
134
134
|
|
|
@@ -153,35 +153,35 @@ interface User {
|
|
|
153
153
|
|
|
154
154
|
#### String Extensions
|
|
155
155
|
|
|
156
|
-
| Extension
|
|
157
|
-
|
|
158
|
-
| `string.email`
|
|
159
|
-
| `string.phone`
|
|
160
|
-
| `string.date`
|
|
161
|
-
| `string.isoDate`
|
|
162
|
-
| `string.uuid`
|
|
163
|
-
| `string.required` | Non-empty (trimmed length >= 1)
|
|
156
|
+
| Extension | Validation |
|
|
157
|
+
| ----------------- | ------------------------------------------- |
|
|
158
|
+
| `string.email` | Email format (`^[^\s@]+@[^\s@]+\.[^\s@]+$`) |
|
|
159
|
+
| `string.phone` | Phone format (`^\+?[0-9\s-]{10,15}$`) |
|
|
160
|
+
| `string.date` | Date string (YYYY-MM-DD, MM/DD/YYYY, etc.) |
|
|
161
|
+
| `string.isoDate` | ISO 8601 date/time |
|
|
162
|
+
| `string.uuid` | UUID v4 format |
|
|
163
|
+
| `string.required` | Non-empty (trimmed length >= 1) |
|
|
164
164
|
|
|
165
165
|
#### Number Extensions
|
|
166
166
|
|
|
167
|
-
| Extension
|
|
168
|
-
|
|
169
|
-
| `number.int`
|
|
170
|
-
| `number.positive`
|
|
171
|
-
| `number.negative`
|
|
172
|
-
| `number.single`
|
|
173
|
-
| `number.double`
|
|
174
|
-
| `number.timestamp`
|
|
175
|
-
| `number.int.positive` | Integer >= 0
|
|
176
|
-
| `number.int.negative` | Integer <= 0
|
|
167
|
+
| Extension | Validation |
|
|
168
|
+
| --------------------- | ---------------------- |
|
|
169
|
+
| `number.int` | Integer (no decimals) |
|
|
170
|
+
| `number.positive` | >= 0 |
|
|
171
|
+
| `number.negative` | <= 0 |
|
|
172
|
+
| `number.single` | Single-precision float |
|
|
173
|
+
| `number.double` | Double-precision float |
|
|
174
|
+
| `number.timestamp` | Integer timestamp |
|
|
175
|
+
| `number.int.positive` | Integer >= 0 |
|
|
176
|
+
| `number.int.negative` | Integer <= 0 |
|
|
177
177
|
|
|
178
178
|
#### Boolean Extensions
|
|
179
179
|
|
|
180
|
-
| Extension
|
|
181
|
-
|
|
180
|
+
| Extension | Validation |
|
|
181
|
+
| ------------------ | -------------- |
|
|
182
182
|
| `boolean.required` | Must be `true` |
|
|
183
|
-
| `boolean.true`
|
|
184
|
-
| `boolean.false`
|
|
183
|
+
| `boolean.true` | Literal true |
|
|
184
|
+
| `boolean.false` | Literal false |
|
|
185
185
|
|
|
186
186
|
## Imports and Exports
|
|
187
187
|
|
|
@@ -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
|
|
|
@@ -55,7 +77,7 @@ All handlers except `phantom` are required. Each handler receives the type with
|
|
|
55
77
|
|
|
56
78
|
## `buildJsonSchema(type)` — Annotated Type → JSON Schema
|
|
57
79
|
|
|
58
|
-
Converts an annotated type into a standard JSON Schema object, translating validation metadata
|
|
80
|
+
Converts an annotated type into a standard JSON Schema object, translating validation metadata. Named object types (those with an `id`) are automatically extracted into `$defs` and referenced via `$ref`:
|
|
59
81
|
|
|
60
82
|
```ts
|
|
61
83
|
import { buildJsonSchema } from '@atscript/typescript/utils'
|
|
@@ -73,29 +95,51 @@ const schema = buildJsonSchema(User)
|
|
|
73
95
|
// }
|
|
74
96
|
```
|
|
75
97
|
|
|
98
|
+
### `$defs` and `$ref`
|
|
99
|
+
|
|
100
|
+
Types compiled from `.as` files carry a stable `id` (the type name). When `buildJsonSchema` encounters named object types nested inside other types (unions, properties), it extracts them into `$defs` and references via `$ref`:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { CatOrDog } from './pets.as'
|
|
104
|
+
const schema = buildJsonSchema(CatOrDog)
|
|
105
|
+
// {
|
|
106
|
+
// $defs: { Cat: { type: 'object', ... }, Dog: { type: 'object', ... } },
|
|
107
|
+
// oneOf: [{ $ref: '#/$defs/Cat' }, { $ref: '#/$defs/Dog' }],
|
|
108
|
+
// discriminator: { propertyName: 'petType', mapping: { cat: '#/$defs/Cat', dog: '#/$defs/Dog' } }
|
|
109
|
+
// }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Key behaviors:
|
|
113
|
+
|
|
114
|
+
- Only **named object types** (with `id`) are extracted to `$defs`. Primitives, unions, arrays stay inline.
|
|
115
|
+
- The **root type** is never extracted — it IS the schema.
|
|
116
|
+
- Same `id` referenced multiple times → one `$defs` entry, all occurrences become `$ref`.
|
|
117
|
+
- Types without `id` (inline/anonymous) produce inline schemas.
|
|
118
|
+
- For programmatic types, use `.id('Name')` on the builder to enable `$defs` extraction.
|
|
119
|
+
|
|
76
120
|
### Metadata → JSON Schema Mapping
|
|
77
121
|
|
|
78
|
-
| Annotation
|
|
79
|
-
|
|
80
|
-
| `@expect.minLength` on string | `minLength`
|
|
81
|
-
| `@expect.maxLength` on string | `maxLength`
|
|
82
|
-
| `@expect.minLength` on array
|
|
83
|
-
| `@expect.maxLength` on array
|
|
84
|
-
| `@expect.min`
|
|
85
|
-
| `@expect.max`
|
|
86
|
-
| `@expect.int`
|
|
87
|
-
| `@expect.pattern` (single)
|
|
88
|
-
| `@expect.pattern` (multiple)
|
|
89
|
-
| `@meta.required` on string
|
|
90
|
-
| optional property
|
|
91
|
-
| union
|
|
92
|
-
| intersection
|
|
93
|
-
| tuple
|
|
94
|
-
| 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) |
|
|
95
139
|
|
|
96
140
|
### Discriminated Unions
|
|
97
141
|
|
|
98
|
-
When all union items are objects sharing exactly one property with distinct const/literal values, `buildJsonSchema` auto-detects it and emits `oneOf` with a `discriminator` object (including `propertyName` and `mapping`) instead of `anyOf`. No annotations needed — detection is automatic.
|
|
142
|
+
When all union items are objects sharing exactly one property with distinct const/literal values, `buildJsonSchema` auto-detects it and emits `oneOf` with a `discriminator` object (including `propertyName` and `mapping`) instead of `anyOf`. When items have `id`, the mapping uses `$ref` paths into `$defs`. No annotations needed — detection is automatic.
|
|
99
143
|
|
|
100
144
|
## `fromJsonSchema(schema)` — JSON Schema → Annotated Type
|
|
101
145
|
|
|
@@ -110,16 +154,33 @@ const type = fromJsonSchema({
|
|
|
110
154
|
name: { type: 'string', minLength: 1 },
|
|
111
155
|
age: { type: 'integer', minimum: 0 },
|
|
112
156
|
},
|
|
113
|
-
required: ['name', 'age']
|
|
157
|
+
required: ['name', 'age'],
|
|
114
158
|
})
|
|
115
159
|
|
|
116
160
|
// The resulting type has a working validator
|
|
117
|
-
type.validator().validate({ name: 'Alice', age: 30 })
|
|
161
|
+
type.validator().validate({ name: 'Alice', age: 30 }) // passes
|
|
118
162
|
```
|
|
119
163
|
|
|
120
|
-
Supports: `type`, `properties`, `required`, `items`, `anyOf`, `oneOf`, `allOf`, `enum`, `const`, `minLength`, `maxLength`, `minimum`, `maximum`, `pattern`, `minItems`, `maxItems`.
|
|
164
|
+
Supports: `type`, `properties`, `required`, `items`, `anyOf`, `oneOf`, `allOf`, `enum`, `const`, `minLength`, `maxLength`, `minimum`, `maximum`, `pattern`, `minItems`, `maxItems`, `$ref`/`$defs`.
|
|
165
|
+
|
|
166
|
+
`$ref` paths are automatically resolved from `$defs` or `definitions` in the schema. Unresolvable `$ref` throws an error.
|
|
167
|
+
|
|
168
|
+
## `mergeJsonSchemas(types)` — Combine Schemas for OpenAPI
|
|
169
|
+
|
|
170
|
+
Combines multiple annotated types into a single schema map with shared `$defs` — useful for building OpenAPI `components/schemas`:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { mergeJsonSchemas } from '@atscript/typescript/utils'
|
|
174
|
+
import { CatOrDog } from './pets.as'
|
|
175
|
+
import { Order } from './orders.as'
|
|
176
|
+
|
|
177
|
+
const merged = mergeJsonSchemas([CatOrDog, Order])
|
|
178
|
+
// merged.schemas.CatOrDog — the CatOrDog schema (oneOf with $ref)
|
|
179
|
+
// merged.schemas.Order — the Order schema
|
|
180
|
+
// merged.$defs: { Cat, Dog, ... } — shared definitions, deduplicated
|
|
181
|
+
```
|
|
121
182
|
|
|
122
|
-
|
|
183
|
+
All types must have an `id` (all types compiled from `.as` files do). The function calls `buildJsonSchema` on each, hoists `$defs` into a shared pool, and returns individual schemas alongside merged definitions.
|
|
123
184
|
|
|
124
185
|
## `serializeAnnotatedType(type, options?)` — Serialize to JSON
|
|
125
186
|
|
|
@@ -171,7 +232,7 @@ type.validator().validate(someData)
|
|
|
171
232
|
type.metadata.get('meta.label')
|
|
172
233
|
```
|
|
173
234
|
|
|
174
|
-
Throws if the serialized version doesn't match `SERIALIZE_VERSION`.
|
|
235
|
+
Throws if the serialized version doesn't match `SERIALIZE_VERSION`. The `id` field is preserved through serialization/deserialization.
|
|
175
236
|
|
|
176
237
|
### `SERIALIZE_VERSION`
|
|
177
238
|
|
|
@@ -250,19 +311,19 @@ const custom = createDataFromAnnotatedType(User, {
|
|
|
250
311
|
mode: (prop, path) => {
|
|
251
312
|
if (path === 'name') return 'John Doe'
|
|
252
313
|
if (path === 'age') return 25
|
|
253
|
-
return undefined
|
|
254
|
-
}
|
|
314
|
+
return undefined // fall through to structural default
|
|
315
|
+
},
|
|
255
316
|
})
|
|
256
317
|
```
|
|
257
318
|
|
|
258
319
|
### Modes
|
|
259
320
|
|
|
260
|
-
| Mode
|
|
261
|
-
|
|
262
|
-
| `'empty'` (default) | Structural defaults: `''`, `0`, `false`, `[]`, `{}`. Optional props omitted
|
|
263
|
-
| `'default'`
|
|
264
|
-
| `'example'`
|
|
265
|
-
| `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 |
|
|
266
327
|
|
|
267
328
|
### Behavior Notes
|
|
268
329
|
|
|
@@ -279,8 +340,8 @@ const custom = createDataFromAnnotatedType(User, {
|
|
|
279
340
|
import { isAnnotatedType } from '@atscript/typescript/utils'
|
|
280
341
|
|
|
281
342
|
if (isAnnotatedType(value)) {
|
|
282
|
-
value.metadata
|
|
283
|
-
value.type
|
|
343
|
+
value.metadata // safe
|
|
344
|
+
value.type // safe
|
|
284
345
|
}
|
|
285
346
|
```
|
|
286
347
|
|
|
@@ -291,10 +352,10 @@ Returns `true` for final types and for unions/intersections/tuples whose all mem
|
|
|
291
352
|
```ts
|
|
292
353
|
import { isAnnotatedTypeOfPrimitive } from '@atscript/typescript/utils'
|
|
293
354
|
|
|
294
|
-
isAnnotatedTypeOfPrimitive(stringType)
|
|
295
|
-
isAnnotatedTypeOfPrimitive(objectType)
|
|
296
|
-
isAnnotatedTypeOfPrimitive(unionOfStringAndNumber)
|
|
297
|
-
isAnnotatedTypeOfPrimitive(unionOfStringAndObject)
|
|
355
|
+
isAnnotatedTypeOfPrimitive(stringType) // true
|
|
356
|
+
isAnnotatedTypeOfPrimitive(objectType) // false
|
|
357
|
+
isAnnotatedTypeOfPrimitive(unionOfStringAndNumber) // true
|
|
358
|
+
isAnnotatedTypeOfPrimitive(unionOfStringAndObject) // false
|
|
298
359
|
```
|
|
299
360
|
|
|
300
361
|
## `isPhantomType(def)` — Check if Phantom
|
|
@@ -302,7 +363,7 @@ isAnnotatedTypeOfPrimitive(unionOfStringAndObject) // false
|
|
|
302
363
|
```ts
|
|
303
364
|
import { isPhantomType } from '@atscript/typescript/utils'
|
|
304
365
|
|
|
305
|
-
isPhantomType(someProperty)
|
|
366
|
+
isPhantomType(someProperty) // true if designType === 'phantom'
|
|
306
367
|
```
|
|
307
368
|
|
|
308
369
|
## `TAtscriptDataType<T>` — Extract DataType from Annotated Type
|
|
@@ -332,8 +393,12 @@ import type { TAtscriptAnnotatedType, TAtscriptDataType } from '@atscript/typesc
|
|
|
332
393
|
|
|
333
394
|
// Generic repository that infers its entity type
|
|
334
395
|
class Repository<T extends TAtscriptAnnotatedType> {
|
|
335
|
-
findOne(id: string): Promise<TAtscriptDataType<T>> {
|
|
336
|
-
|
|
396
|
+
findOne(id: string): Promise<TAtscriptDataType<T>> {
|
|
397
|
+
/* ... */
|
|
398
|
+
}
|
|
399
|
+
insertOne(data: TAtscriptDataType<T>): Promise<void> {
|
|
400
|
+
/* ... */
|
|
401
|
+
}
|
|
337
402
|
}
|
|
338
403
|
|
|
339
404
|
// Usage — DataType is automatically inferred
|
|
@@ -360,25 +425,25 @@ Key types you may need to import:
|
|
|
360
425
|
|
|
361
426
|
```ts
|
|
362
427
|
import type {
|
|
363
|
-
TAtscriptAnnotatedType,
|
|
428
|
+
TAtscriptAnnotatedType, // core annotated type
|
|
364
429
|
TAtscriptAnnotatedTypeConstructor, // annotated type that's also a class
|
|
365
|
-
TAtscriptTypeDef,
|
|
366
|
-
TAtscriptTypeFinal,
|
|
367
|
-
TAtscriptTypeObject,
|
|
368
|
-
TAtscriptTypeArray,
|
|
369
|
-
TAtscriptTypeComplex,
|
|
370
|
-
TMetadataMap,
|
|
371
|
-
TAnnotatedTypeHandle,
|
|
372
|
-
InferDataType,
|
|
373
|
-
TAtscriptDataType,
|
|
374
|
-
TValidatorOptions,
|
|
375
|
-
TValidatorPlugin,
|
|
376
|
-
TValidatorPluginContext,
|
|
377
|
-
TSerializedAnnotatedType,
|
|
378
|
-
TSerializeOptions,
|
|
379
|
-
TFlattenOptions,
|
|
380
|
-
TCreateDataOptions,
|
|
381
|
-
TValueResolver,
|
|
382
|
-
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
|
|
383
448
|
} from '@atscript/typescript/utils'
|
|
384
449
|
```
|