@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.
@@ -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, mergeJsonSchemas,
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
 
@@ -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 | JSON Schema |
100
- |-----------|-------------|
101
- | `@expect.minLength` on string | `minLength` |
102
- | `@expect.maxLength` on string | `maxLength` |
103
- | `@expect.minLength` on array | `minItems` |
104
- | `@expect.maxLength` on array | `maxItems` |
105
- | `@expect.min` | `minimum` |
106
- | `@expect.max` | `maximum` |
107
- | `@expect.int` | `type: 'integer'` (instead of `'number'`) |
108
- | `@expect.pattern` (single) | `pattern` |
109
- | `@expect.pattern` (multiple) | `allOf: [{ pattern }, ...]` |
110
- | `@meta.required` on string | `minLength: 1` |
111
- | optional property | not in `required` array |
112
- | union | `anyOf` (or `oneOf` + `discriminator` for discriminated unions) |
113
- | intersection | `allOf` |
114
- | tuple | `items` as array |
115
- | 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) |
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 }) // passes
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 // fall through to structural default
292
- }
314
+ return undefined // fall through to structural default
315
+ },
293
316
  })
294
317
  ```
295
318
 
296
319
  ### Modes
297
320
 
298
- | Mode | Behavior |
299
- |------|----------|
300
- | `'empty'` (default) | Structural defaults: `''`, `0`, `false`, `[]`, `{}`. Optional props omitted |
301
- | `'default'` | Uses `@meta.default` annotations. Optional props only included if annotated |
302
- | `'example'` | Uses `@meta.example` annotations. Optional props always included. Arrays get one sample item |
303
- | `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 |
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 // safe
321
- value.type // safe
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) // true
333
- isAnnotatedTypeOfPrimitive(objectType) // false
334
- isAnnotatedTypeOfPrimitive(unionOfStringAndNumber) // true
335
- isAnnotatedTypeOfPrimitive(unionOfStringAndObject) // false
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) // true if designType === 'phantom'
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
- insertOne(data: TAtscriptDataType<T>): Promise<void> { /* ... */ }
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, // core annotated type
428
+ TAtscriptAnnotatedType, // core annotated type
402
429
  TAtscriptAnnotatedTypeConstructor, // annotated type that's also a class
403
- TAtscriptTypeDef, // union of all type def shapes
404
- TAtscriptTypeFinal, // primitive/literal type def
405
- TAtscriptTypeObject, // object type def
406
- TAtscriptTypeArray, // array type def
407
- TAtscriptTypeComplex, // union/intersection/tuple type def
408
- TMetadataMap, // typed metadata map
409
- TAnnotatedTypeHandle, // fluent builder handle
410
- InferDataType, // extract DataType from a type def's phantom generic
411
- TAtscriptDataType, // extract DataType from TAtscriptAnnotatedType
412
- TValidatorOptions, // validator config
413
- TValidatorPlugin, // plugin function type
414
- TValidatorPluginContext, // plugin context
415
- TSerializedAnnotatedType, // serialized type (top-level)
416
- TSerializeOptions, // serialization options
417
- TFlattenOptions, // flatten options
418
- TCreateDataOptions, // createData options
419
- TValueResolver, // custom resolver for createData
420
- 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
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 // TypeScript knows this exists
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, // 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
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 // 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)
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 | 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 |
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 // 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)
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 | 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 |
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
  ```