@atscript/typescript 0.1.49 → 0.1.51

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.
@@ -1,452 +0,0 @@
1
- # Utility Functions — @atscript/typescript
2
-
3
- > All publicly exported utility functions: serialization, flattening, JSON Schema, data creation, and type traversal.
4
-
5
- ## Exports Overview
6
-
7
- All utilities are exported from `@atscript/typescript/utils`:
8
-
9
- ```ts
10
- import {
11
- // Type construction
12
- defineAnnotatedType,
13
- annotate,
14
- // Type checking
15
- isAnnotatedType,
16
- isAnnotatedTypeOfPrimitive,
17
- isPhantomType,
18
- // Type traversal
19
- forAnnotatedType,
20
- // Validation
21
- Validator,
22
- ValidatorError,
23
- // JSON Schema
24
- buildJsonSchema,
25
- fromJsonSchema,
26
- mergeJsonSchemas,
27
- // Serialization
28
- serializeAnnotatedType,
29
- deserializeAnnotatedType,
30
- SERIALIZE_VERSION,
31
- // Flattening
32
- flattenAnnotatedType,
33
- // Data creation
34
- createDataFromAnnotatedType,
35
- // Feature gating (used by generated code)
36
- throwFeatureDisabled,
37
- } from '@atscript/typescript/utils'
38
- ```
39
-
40
- ### `throwFeatureDisabled(feature, option, annotation)`
41
-
42
- Throws a runtime error indicating a feature is disabled. Used by generated `.js` files to avoid duplicating the error message string across all classes. Called as `$d("JSON Schema", "jsonSchema", "emit.jsonSchema")` in generated code when `jsonSchema: false`.
43
-
44
- ## `forAnnotatedType(def, handlers)` — Type-Safe Dispatch
45
-
46
- Dispatches over `TAtscriptAnnotatedType` by its `type.kind`, providing type-narrowed handlers:
47
-
48
- ```ts
49
- import { forAnnotatedType } from '@atscript/typescript/utils'
50
-
51
- const description = forAnnotatedType(someType, {
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
73
- })
74
- ```
75
-
76
- All handlers except `phantom` are required. Each handler receives the type with its `type` field narrowed to the specific kind.
77
-
78
- ## `buildJsonSchema(type)` — Annotated Type → JSON Schema
79
-
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`:
81
-
82
- ```ts
83
- import { buildJsonSchema } from '@atscript/typescript/utils'
84
- import { User } from './models/user.as'
85
-
86
- const schema = buildJsonSchema(User)
87
- // {
88
- // type: 'object',
89
- // properties: {
90
- // name: { type: 'string', minLength: 2, maxLength: 100 },
91
- // age: { type: 'integer', minimum: 0, maximum: 150 },
92
- // email: { type: 'string', pattern: '...' },
93
- // },
94
- // required: ['name', 'age']
95
- // }
96
- ```
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
-
120
- ### Metadata → JSON Schema Mapping
121
-
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) |
139
-
140
- ### Discriminated Unions
141
-
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.
143
-
144
- ## `fromJsonSchema(schema)` — JSON Schema → Annotated Type
145
-
146
- The inverse of `buildJsonSchema`. Creates a fully functional annotated type from a JSON Schema:
147
-
148
- ```ts
149
- import { fromJsonSchema } from '@atscript/typescript/utils'
150
-
151
- const type = fromJsonSchema({
152
- type: 'object',
153
- properties: {
154
- name: { type: 'string', minLength: 1 },
155
- age: { type: 'integer', minimum: 0 },
156
- },
157
- required: ['name', 'age'],
158
- })
159
-
160
- // The resulting type has a working validator
161
- type.validator().validate({ name: 'Alice', age: 30 }) // passes
162
- ```
163
-
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
- ```
182
-
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.
184
-
185
- ## `serializeAnnotatedType(type, options?)` — Serialize to JSON
186
-
187
- Converts a runtime annotated type into a plain JSON-safe object for storage or transmission:
188
-
189
- ```ts
190
- import { serializeAnnotatedType } from '@atscript/typescript/utils'
191
-
192
- const json = serializeAnnotatedType(User)
193
- // json is a plain object safe for JSON.stringify()
194
- const str = JSON.stringify(json)
195
- ```
196
-
197
- ### Serialization Options
198
-
199
- ```ts
200
- serializeAnnotatedType(User, {
201
- // Strip specific annotation keys
202
- ignoreAnnotations: ['meta.sensitive', 'mongo.collection'],
203
-
204
- // Advanced per-annotation transform
205
- processAnnotation(ctx) {
206
- // ctx.key — annotation key (e.g. 'meta.label')
207
- // ctx.value — annotation value
208
- // ctx.path — property path (e.g. ['address', 'city'])
209
- // ctx.kind — type kind at this node
210
-
211
- // Return { key, value } to keep (possibly transformed)
212
- // Return undefined to strip
213
- if (ctx.key.startsWith('mongo.')) return undefined
214
- return { key: ctx.key, value: ctx.value }
215
- },
216
-
217
- // Include FK references (0 = strip, 1 = immediate refs, 2+ = deeper)
218
- refDepth: 1,
219
- })
220
- ```
221
-
222
- ## `deserializeAnnotatedType(data)` — Restore from JSON
223
-
224
- Restores a fully functional annotated type from its serialized form:
225
-
226
- ```ts
227
- import { deserializeAnnotatedType } from '@atscript/typescript/utils'
228
-
229
- const type = deserializeAnnotatedType(json)
230
-
231
- // Fully functional — validator works
232
- type.validator().validate(someData)
233
-
234
- // Metadata accessible
235
- type.metadata.get('meta.label')
236
- ```
237
-
238
- Throws if the serialized version doesn't match `SERIALIZE_VERSION`. The `id` field is preserved through serialization/deserialization.
239
-
240
- ### `SERIALIZE_VERSION`
241
-
242
- Current serialization format version (currently `2`). Used for forward compatibility:
243
-
244
- ```ts
245
- import { SERIALIZE_VERSION } from '@atscript/typescript/utils'
246
- ```
247
-
248
- ## `flattenAnnotatedType(type, options?)` — Flatten to Dot-Path Map
249
-
250
- Flattens a nested object type into a `Map<string, TAtscriptAnnotatedType>` keyed by dot-separated paths:
251
-
252
- ```ts
253
- import { flattenAnnotatedType } from '@atscript/typescript/utils'
254
-
255
- const flat = flattenAnnotatedType(User)
256
- // Map {
257
- // '' → root object type
258
- // 'name' → string type (with metadata)
259
- // 'age' → number type
260
- // 'address' → nested object type
261
- // 'address.street' → string type
262
- // 'address.city' → string type
263
- // }
264
-
265
- for (const [path, type] of flat) {
266
- const label = type.metadata.get('meta.label')
267
- console.log(path || '(root)', label)
268
- }
269
- ```
270
-
271
- ### Flatten Options
272
-
273
- ```ts
274
- flattenAnnotatedType(User, {
275
- // Callback for each field (non-root)
276
- onField(path, type, metadata) {
277
- console.log(`Field: ${path}`)
278
- },
279
-
280
- // Tag top-level array fields with a metadata key
281
- topLevelArrayTag: 'mongo.__topLevelArray',
282
-
283
- // Skip phantom types
284
- excludePhantomTypes: true,
285
- })
286
- ```
287
-
288
- ### How Flattening Handles Complex Types
289
-
290
- - **Objects**: recursed into — each property gets its own path
291
- - **Arrays**: recursed into — element type's properties share the array's path prefix
292
- - **Unions/Intersections/Tuples**: recursed into — if the same path appears in multiple branches, they're merged into a synthetic union
293
- - **Primitives**: added directly at their path
294
-
295
- ## `createDataFromAnnotatedType(type, options?)` — Create Default Data
296
-
297
- Creates a data object matching the type's shape, using structural defaults or annotation values:
298
-
299
- ```ts
300
- import { createDataFromAnnotatedType } from '@atscript/typescript/utils'
301
-
302
- // Empty structural defaults ('', 0, false, [], {})
303
- const empty = createDataFromAnnotatedType(User)
304
- // { name: '', age: 0, active: false, address: { street: '', city: '' } }
305
-
306
- // Use @meta.default annotations
307
- const defaults = createDataFromAnnotatedType(User, { mode: 'default' })
308
-
309
- // Use @meta.example annotations
310
- const example = createDataFromAnnotatedType(User, { mode: 'example' })
311
-
312
- // Custom resolver function
313
- const custom = createDataFromAnnotatedType(User, {
314
- mode: (prop, path) => {
315
- if (path === 'name') return 'John Doe'
316
- if (path === 'age') return 25
317
- return undefined // fall through to structural default
318
- },
319
- })
320
- ```
321
-
322
- ### Modes
323
-
324
- | Mode | Behavior |
325
- | ------------------- | -------------------------------------------------------------------------------------------- |
326
- | `'empty'` (default) | Structural defaults: `''`, `0`, `false`, `[]`, `{}`. Optional props omitted |
327
- | `'default'` | Uses `@meta.default` annotations. Optional props only included if annotated |
328
- | `'example'` | Uses `@meta.example` annotations. Optional props always included. Arrays get one sample item |
329
- | `function` | Custom resolver per field. Return `undefined` to fall through |
330
-
331
- ### Behavior Notes
332
-
333
- - **Optional properties** are omitted unless the mode provides a value for them (exception: `'example'` mode always includes all optional props)
334
- - **Arrays** in `'example'` mode generate one sample item from the element type instead of an empty array
335
- - **Complex types** (object, array): if a `@meta.default`/`@meta.example` annotation is set and passes validation, the entire subtree is replaced (no recursion into inner props)
336
- - **Annotation values**: strings are used as-is for string types; everything else is parsed via `JSON.parse`
337
- - **Unions/Intersections**: defaults to first item's value
338
- - **Phantom types**: skipped
339
-
340
- ## `isAnnotatedType(value)` — Type Guard
341
-
342
- ```ts
343
- import { isAnnotatedType } from '@atscript/typescript/utils'
344
-
345
- if (isAnnotatedType(value)) {
346
- value.metadata // safe
347
- value.type // safe
348
- }
349
- ```
350
-
351
- ## `isAnnotatedTypeOfPrimitive(type)` — Check if Primitive
352
-
353
- Returns `true` for final types and for unions/intersections/tuples whose all members are primitives:
354
-
355
- ```ts
356
- import { isAnnotatedTypeOfPrimitive } from '@atscript/typescript/utils'
357
-
358
- isAnnotatedTypeOfPrimitive(stringType) // true
359
- isAnnotatedTypeOfPrimitive(objectType) // false
360
- isAnnotatedTypeOfPrimitive(unionOfStringAndNumber) // true
361
- isAnnotatedTypeOfPrimitive(unionOfStringAndObject) // false
362
- ```
363
-
364
- ## `isPhantomType(def)` — Check if Phantom
365
-
366
- ```ts
367
- import { isPhantomType } from '@atscript/typescript/utils'
368
-
369
- isPhantomType(someProperty) // true if designType === 'phantom'
370
- ```
371
-
372
- ## `TAtscriptDataType<T>` — Extract DataType from Annotated Type
373
-
374
- Utility type that extracts the underlying data shape from a `TAtscriptAnnotatedType`. This is the primary way to obtain a TypeScript data type from an Atscript-generated class, especially useful in generic contexts.
375
-
376
- ```ts
377
- import type { TAtscriptDataType } from '@atscript/typescript/utils'
378
- import { Product } from './product.as'
379
-
380
- type ProductData = TAtscriptDataType<typeof Product>
381
- // ProductData = { name: string; price: number; tags: string[] }
382
- ```
383
-
384
- ### How It Resolves
385
-
386
- 1. Extracts the phantom `__dataType` from the type definition
387
- 2. If `__dataType` is `unknown` (unset), falls back to the constructor's instance type (`T extends new (...) => infer I`)
388
- 3. Otherwise returns `unknown`
389
-
390
- ### Use in Generics
391
-
392
- `TAtscriptDataType` is designed for generic code that needs to derive data types from annotated type parameters:
393
-
394
- ```ts
395
- import type { TAtscriptAnnotatedType, TAtscriptDataType } from '@atscript/typescript/utils'
396
-
397
- // Generic repository that infers its entity type
398
- class Repository<T extends TAtscriptAnnotatedType> {
399
- findOne(id: string): Promise<TAtscriptDataType<T>> {
400
- /* ... */
401
- }
402
- insertOne(data: TAtscriptDataType<T>): Promise<void> {
403
- /* ... */
404
- }
405
- }
406
-
407
- // Usage — DataType is automatically inferred
408
- const repo = new Repository<typeof Product>()
409
- const product = await repo.findOne('123') // typed as Product
410
-
411
- // Generic function
412
- function validate<T extends TAtscriptAnnotatedType>(
413
- schema: T,
414
- data: unknown
415
- ): data is TAtscriptDataType<T> {
416
- return schema.validator().validate(data, true)
417
- }
418
- ```
419
-
420
- ### Difference from `InferDataType`
421
-
422
- - `TAtscriptDataType<T>` — operates on `TAtscriptAnnotatedType` (the full annotated wrapper). Use this for generated classes and generic code.
423
- - `InferDataType<T>` — operates on raw type definitions (`TAtscriptTypeDef`, `TAtscriptTypeObject`, etc.). Lower-level, extracts `__dataType` from the type def's phantom generic directly.
424
-
425
- ## Type Exports
426
-
427
- Key types you may need to import:
428
-
429
- ```ts
430
- import type {
431
- TAtscriptAnnotatedType, // core annotated type
432
- TAtscriptAnnotatedTypeConstructor, // annotated type that's also a class
433
- TAtscriptTypeDef, // union of all type def shapes
434
- TAtscriptTypeFinal, // primitive/literal type def
435
- TAtscriptTypeObject, // object type def
436
- TAtscriptTypeArray, // array type def
437
- TAtscriptTypeComplex, // union/intersection/tuple type def
438
- TMetadataMap, // typed metadata map
439
- TAnnotatedTypeHandle, // fluent builder handle
440
- InferDataType, // extract DataType from a type def's phantom generic
441
- TAtscriptDataType, // extract DataType from TAtscriptAnnotatedType
442
- TValidatorOptions, // validator config
443
- TValidatorPlugin, // plugin function type
444
- TValidatorPluginContext, // plugin context
445
- TSerializedAnnotatedType, // serialized type (top-level)
446
- TSerializeOptions, // serialization options
447
- TFlattenOptions, // flatten options
448
- TCreateDataOptions, // createData options
449
- TValueResolver, // custom resolver for createData
450
- TJsonSchema, // JSON Schema object
451
- } from '@atscript/typescript/utils'
452
- ```