@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.
@@ -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 // 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
- validator(opts?): Validator // create a validator instance
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 // for literal types
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> // named properties
51
- propsPatterns: Array<{ pattern: RegExp; def: TAtscriptAnnotatedType }> // pattern properties
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 // element type
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') // string | undefined
85
- const required = User.metadata.get('meta.required') // { message?: string } | true | undefined
86
- const minLen = User.metadata.get('expect.minLength') // { length: number; message?: string } | undefined
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') // boolean
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') // "Full Name"
107
- nameProp.metadata.get('meta.required') // true or { message: "..." }
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 // boolean | undefined
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 '': // final/primitive
151
+ case '': // final/primitive
151
152
  console.log('Primitive:', def.type.designType)
152
153
  break
153
- case 'object': // object with props
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': // 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) { return `primitive: ${d.type.designType}` },
179
- object(d) { return `object with ${d.type.props.size} props` },
180
- array(d) { return `array` },
181
- union(d) { return `union of ${d.type.items.length}` },
182
- intersection(d) { return `intersection of ${d.type.items.length}` },
183
- tuple(d) { return `tuple of ${d.type.items.length}` },
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) { return `phantom` },
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') // true
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) // true if no objects or arrays
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
- .of(defineAnnotatedType().designType('string').$type)
246
- .$type
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().designType('string')
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 | Description |
264
- |--------|-------------|
265
- | `.designType(dt)` | Set primitive design type |
266
- | `.value(v)` | Set literal value |
267
- | `.tags(...tags)` | Add primitive tags |
268
- | `.prop(name, type)` | Add named property (object kind) |
269
- | `.propPattern(regex, type)` | Add pattern property (object kind) |
270
- | `.of(type)` | Set element type (array kind) |
271
- | `.item(type)` | Add item (union/intersection/tuple kind) |
272
- | `.optional(flag?)` | Mark as optional |
273
- | `.annotate(key, value, asArray?)` | Set metadata annotation |
274
- | `.copyMetadata(from, ignore?)` | Copy metadata from another type |
275
- | `.refTo(type, chain?)` | Reference another annotated type's definition |
276
- | `.$type` | Get the final `TAtscriptAnnotatedType` |
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 | 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) |
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 | 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) |
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 | 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 |
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 | Validation |
181
- |-----------|-----------|
180
+ | Extension | Validation |
181
+ | ------------------ | -------------- |
182
182
  | `boolean.required` | Must be `true` |
183
- | `boolean.true` | Literal true |
184
- | `boolean.false` | Literal 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, annotate,
12
+ defineAnnotatedType,
13
+ annotate,
13
14
  // Type checking
14
- isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType,
15
+ isAnnotatedType,
16
+ isAnnotatedTypeOfPrimitive,
17
+ isPhantomType,
15
18
  // Type traversal
16
19
  forAnnotatedType,
17
20
  // Validation
18
- Validator, ValidatorError,
21
+ Validator,
22
+ ValidatorError,
19
23
  // JSON Schema
20
- buildJsonSchema, fromJsonSchema,
24
+ buildJsonSchema,
25
+ fromJsonSchema,
26
+ mergeJsonSchemas,
21
27
  // Serialization
22
- serializeAnnotatedType, deserializeAnnotatedType, SERIALIZE_VERSION,
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) { return `${d.type.designType}` },
45
- object(d) { return `object(${d.type.props.size} props)` },
46
- array(d) { return `array` },
47
- union(d) { return `union(${d.type.items.length})` },
48
- intersection(d) { return `intersection(${d.type.items.length})` },
49
- tuple(d) { return `[${d.type.items.length}]` },
50
- phantom(d) { return `phantom` }, // optional — without it, phantoms go to final
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 | JSON Schema |
79
- |-----------|-------------|
80
- | `@expect.minLength` on string | `minLength` |
81
- | `@expect.maxLength` on string | `maxLength` |
82
- | `@expect.minLength` on array | `minItems` |
83
- | `@expect.maxLength` on array | `maxItems` |
84
- | `@expect.min` | `minimum` |
85
- | `@expect.max` | `maximum` |
86
- | `@expect.int` | `type: 'integer'` (instead of `'number'`) |
87
- | `@expect.pattern` (single) | `pattern` |
88
- | `@expect.pattern` (multiple) | `allOf: [{ pattern }, ...]` |
89
- | `@meta.required` on string | `minLength: 1` |
90
- | optional property | not in `required` array |
91
- | union | `anyOf` (or `oneOf` + `discriminator` for discriminated unions) |
92
- | intersection | `allOf` |
93
- | tuple | `items` as array |
94
- | phantom | empty object `{}` (excluded) |
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 }) // passes
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
- Does **not** support `$ref` dereference schemas first.
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 // fall through to structural default
254
- }
314
+ return undefined // fall through to structural default
315
+ },
255
316
  })
256
317
  ```
257
318
 
258
319
  ### Modes
259
320
 
260
- | Mode | Behavior |
261
- |------|----------|
262
- | `'empty'` (default) | Structural defaults: `''`, `0`, `false`, `[]`, `{}`. Optional props omitted |
263
- | `'default'` | Uses `@meta.default` annotations. Optional props only included if annotated |
264
- | `'example'` | Uses `@meta.example` annotations. Optional props always included. Arrays get one sample item |
265
- | `function` | Custom resolver per field. Return `undefined` to fall through |
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 // safe
283
- value.type // safe
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) // true
295
- isAnnotatedTypeOfPrimitive(objectType) // false
296
- isAnnotatedTypeOfPrimitive(unionOfStringAndNumber) // true
297
- isAnnotatedTypeOfPrimitive(unionOfStringAndObject) // false
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) // true if designType === 'phantom'
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
- insertOne(data: TAtscriptDataType<T>): Promise<void> { /* ... */ }
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, // core annotated type
428
+ TAtscriptAnnotatedType, // core annotated type
364
429
  TAtscriptAnnotatedTypeConstructor, // annotated type that's also a class
365
- TAtscriptTypeDef, // union of all type def shapes
366
- TAtscriptTypeFinal, // primitive/literal type def
367
- TAtscriptTypeObject, // object type def
368
- TAtscriptTypeArray, // array type def
369
- TAtscriptTypeComplex, // union/intersection/tuple type def
370
- TMetadataMap, // typed metadata map
371
- TAnnotatedTypeHandle, // fluent builder handle
372
- InferDataType, // extract DataType from a type def's phantom generic
373
- TAtscriptDataType, // extract DataType from TAtscriptAnnotatedType
374
- TValidatorOptions, // validator config
375
- TValidatorPlugin, // plugin function type
376
- TValidatorPluginContext, // plugin context
377
- TSerializedAnnotatedType, // serialized type (top-level)
378
- TSerializeOptions, // serialization options
379
- TFlattenOptions, // flatten options
380
- TCreateDataOptions, // createData options
381
- TValueResolver, // custom resolver for createData
382
- TJsonSchema, // JSON Schema object
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
  ```