@kubb/ast 5.0.0-beta.55 → 5.0.0-beta.57

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,3 +1,4 @@
1
+ import { defineNode, syncOptionality } from '../node.ts'
1
2
  import type { BaseNode } from './base.ts'
2
3
  import type { SchemaNode } from './schema.ts'
3
4
 
@@ -32,3 +33,38 @@ export type PropertyNode = BaseNode & {
32
33
  */
33
34
  required: boolean
34
35
  }
36
+
37
+ /**
38
+ * Loosely-typed property accepted by `createProperty`, with `required` optional.
39
+ */
40
+ export type UserPropertyNode = Pick<PropertyNode, 'name' | 'schema'> & Partial<Omit<PropertyNode, 'kind' | 'name' | 'schema'>>
41
+
42
+ /**
43
+ * Definition for the {@link PropertyNode}. `required` defaults to `false` and the
44
+ * schema's `optional`/`nullish` flags are kept in sync with it.
45
+ */
46
+ export const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
47
+ kind: 'Property',
48
+ build: (props) => {
49
+ const required = props.required ?? false
50
+ return { ...props, required, schema: syncOptionality(props.schema, required) }
51
+ },
52
+ children: ['schema'],
53
+ visitorKey: 'property',
54
+ rebuild: true,
55
+ })
56
+
57
+ /**
58
+ * Creates a `PropertyNode`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const property = createProperty({
63
+ * name: 'status',
64
+ * required: true,
65
+ * schema: createSchema({ type: 'string', nullable: true }),
66
+ * })
67
+ * // required=true, no optional/nullish
68
+ * ```
69
+ */
70
+ export const createProperty = propertyDef.create
@@ -1,5 +1,6 @@
1
+ import { defineNode } from '../node.ts'
1
2
  import type { BaseNode } from './base.ts'
2
- import type { ContentNode } from './content.ts'
3
+ import { type ContentNode, createContent, type UserContent } from './content.ts'
3
4
 
4
5
  /**
5
6
  * AST node representing an operation request body.
@@ -40,3 +41,24 @@ export type RequestBodyNode = BaseNode & {
40
41
  */
41
42
  content?: Array<ContentNode>
42
43
  }
44
+
45
+ /**
46
+ * Loosely-typed request body accepted by `createOperation`, normalized into a {@link RequestBodyNode}.
47
+ */
48
+ export type UserRequestBody = Omit<RequestBodyNode, 'kind' | 'content'> & {
49
+ content?: Array<UserContent>
50
+ }
51
+
52
+ /**
53
+ * Definition for the {@link RequestBodyNode}, normalizing each content entry into a `ContentNode`.
54
+ */
55
+ export const requestBodyDef = defineNode<RequestBodyNode, UserRequestBody>({
56
+ kind: 'RequestBody',
57
+ build: (props) => ({ ...props, content: props.content?.map(createContent) }),
58
+ children: ['content'],
59
+ })
60
+
61
+ /**
62
+ * Creates a `RequestBodyNode`, normalizing each content entry into a `ContentNode`.
63
+ */
64
+ export const createRequestBody = requestBodyDef.create
@@ -1,6 +1,8 @@
1
+ import { defineNode } from '../node.ts'
1
2
  import type { BaseNode } from './base.ts'
2
- import type { ContentNode } from './content.ts'
3
+ import { type ContentNode, createContent, type UserContent } from './content.ts'
3
4
  import type { StatusCode } from './http.ts'
5
+ import type { SchemaNode } from './schema.ts'
4
6
 
5
7
  /**
6
8
  * AST node representing one operation response variant.
@@ -48,3 +50,39 @@ export type ResponseNode = BaseNode & {
48
50
  */
49
51
  content?: Array<ContentNode>
50
52
  }
53
+
54
+ type ResponseInput = Pick<ResponseNode, 'statusCode'> &
55
+ Partial<Omit<ResponseNode, 'kind' | 'statusCode' | 'content'>> & {
56
+ content?: Array<UserContent>
57
+ schema?: SchemaNode
58
+ mediaType?: string | null
59
+ keysToOmit?: Array<string> | null
60
+ }
61
+
62
+ /**
63
+ * Definition for the {@link ResponseNode}. A single legacy `schema` (with optional
64
+ * `mediaType`/`keysToOmit`) is normalized into one `content` entry.
65
+ */
66
+ export const responseDef = defineNode<ResponseNode, ResponseInput>({
67
+ kind: 'Response',
68
+ build: (props) => {
69
+ const { schema, mediaType, keysToOmit, content, ...rest } = props
70
+ const entries = content ?? (schema ? [{ contentType: mediaType ?? 'application/json', schema, keysToOmit: keysToOmit ?? null }] : undefined)
71
+ return { ...rest, content: entries?.map(createContent) }
72
+ },
73
+ children: ['content'],
74
+ visitorKey: 'response',
75
+ })
76
+
77
+ /**
78
+ * Creates a `ResponseNode`.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const response = createResponse({
83
+ * statusCode: '200',
84
+ * content: [{ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) }],
85
+ * })
86
+ * ```
87
+ */
88
+ export const createResponse = responseDef.create
@@ -1,3 +1,5 @@
1
+ import type { InferSchemaNode } from '../infer.ts'
2
+ import { defineNode, type DistributiveOmit } from '../node.ts'
1
3
  import type { BaseNode } from './base.ts'
2
4
  import type { PropertyNode } from './property.ts'
3
5
 
@@ -657,3 +659,73 @@ export type SchemaNode =
657
659
  | Ipv4SchemaNode
658
660
  | Ipv6SchemaNode
659
661
  | ScalarSchemaNode
662
+
663
+ type CreateSchemaObjectInput = Omit<ObjectSchemaNode, 'kind' | 'properties' | 'primitive'> & { properties?: Array<PropertyNode>; primitive?: 'object' }
664
+ type CreateSchemaInput = CreateSchemaObjectInput | DistributiveOmit<Exclude<SchemaNode, ObjectSchemaNode>, 'kind'>
665
+ type CreateSchemaOutput<T extends CreateSchemaInput> = InferSchemaNode<T> & {
666
+ kind: 'Schema'
667
+ }
668
+
669
+ /**
670
+ * Maps schema `type` to its underlying `primitive`.
671
+ * Primitive types map to themselves. Special string formats map to `'string'`.
672
+ * Complex types (`ref`, `enum`, `union`, `intersection`, `tuple`, `blob`) are left unset.
673
+ */
674
+ const TYPE_TO_PRIMITIVE: Partial<Record<SchemaNode['type'], PrimitiveSchemaType>> = {
675
+ string: 'string',
676
+ number: 'number',
677
+ integer: 'integer',
678
+ bigint: 'bigint',
679
+ boolean: 'boolean',
680
+ null: 'null',
681
+ any: 'any',
682
+ unknown: 'unknown',
683
+ void: 'void',
684
+ never: 'never',
685
+ object: 'object',
686
+ array: 'array',
687
+ date: 'date',
688
+ uuid: 'string',
689
+ email: 'string',
690
+ url: 'string',
691
+ datetime: 'string',
692
+ time: 'string',
693
+ }
694
+
695
+ /**
696
+ * Definition for the {@link SchemaNode}. Object schemas default `properties` to an
697
+ * empty array, and `primitive` is inferred from `type` when not explicitly provided.
698
+ */
699
+ export const schemaDef = defineNode<SchemaNode, CreateSchemaInput>({
700
+ kind: 'Schema',
701
+ build: (props) => {
702
+ if (props.type === 'object') {
703
+ return { properties: [], primitive: 'object' as const, ...props }
704
+ }
705
+
706
+ return { primitive: TYPE_TO_PRIMITIVE[props.type as keyof typeof TYPE_TO_PRIMITIVE], ...props }
707
+ },
708
+ children: ['properties', 'items', 'members', 'additionalProperties'],
709
+ visitorKey: 'schema',
710
+ })
711
+
712
+ /**
713
+ * Creates a `SchemaNode`, narrowed to the variant of `props.type`.
714
+ *
715
+ * @example
716
+ * ```ts
717
+ * const scalar = createSchema({ type: 'string' })
718
+ * // { kind: 'Schema', type: 'string', primitive: 'string' }
719
+ * ```
720
+ *
721
+ * @example
722
+ * ```ts
723
+ * const object = createSchema({ type: 'object' })
724
+ * // { kind: 'Schema', type: 'object', primitive: 'object', properties: [] }
725
+ * ```
726
+ */
727
+ export function createSchema<T extends CreateSchemaInput>(props: T): CreateSchemaOutput<T>
728
+ export function createSchema(props: CreateSchemaInput): SchemaNode
729
+ export function createSchema(props: CreateSchemaInput): SchemaNode {
730
+ return schemaDef.create(props)
731
+ }
@@ -0,0 +1,70 @@
1
+ import type { NodeDef } from './node.ts'
2
+ import { arrowFunctionDef, breakDef, constDef, functionDef, jsxDef, textDef, typeDef } from './nodes/code.ts'
3
+ import { contentDef } from './nodes/content.ts'
4
+ import { exportDef, fileDef, importDef, sourceDef } from './nodes/file.ts'
5
+ import { functionParameterDef, functionParametersDef, indexedAccessTypeDef, objectBindingPatternDef, typeLiteralDef } from './nodes/function.ts'
6
+ import type { Node, NodeKind } from './nodes/index.ts'
7
+ import { inputDef } from './nodes/input.ts'
8
+ import { operationDef } from './nodes/operation.ts'
9
+ import { outputDef } from './nodes/output.ts'
10
+ import { parameterDef } from './nodes/parameter.ts'
11
+ import { propertyDef } from './nodes/property.ts'
12
+ import { requestBodyDef } from './nodes/requestBody.ts'
13
+ import { responseDef } from './nodes/response.ts'
14
+ import { schemaDef } from './nodes/schema.ts'
15
+
16
+ /**
17
+ * Every node definition. Adding a node means adding its `defineNode` to one
18
+ * `nodes/*.ts` file and listing it here. The visitor tables below derive from it.
19
+ */
20
+ export const nodeDefs = [
21
+ inputDef,
22
+ outputDef,
23
+ operationDef,
24
+ requestBodyDef,
25
+ contentDef,
26
+ responseDef,
27
+ schemaDef,
28
+ propertyDef,
29
+ parameterDef,
30
+ functionParameterDef,
31
+ functionParametersDef,
32
+ typeLiteralDef,
33
+ indexedAccessTypeDef,
34
+ objectBindingPatternDef,
35
+ constDef,
36
+ typeDef,
37
+ functionDef,
38
+ arrowFunctionDef,
39
+ textDef,
40
+ breakDef,
41
+ jsxDef,
42
+ importDef,
43
+ exportDef,
44
+ sourceDef,
45
+ fileDef,
46
+ ] satisfies ReadonlyArray<NodeDef>
47
+
48
+ /**
49
+ * Child node fields per node kind, in traversal order (Babel's `VISITOR_KEYS`).
50
+ * Derived from each definition's `children`.
51
+ */
52
+ export const VISITOR_KEYS = Object.fromEntries(nodeDefs.flatMap((def) => (def.children ? [[def.kind, def.children] as const] : []))) as Partial<
53
+ Record<NodeKind, ReadonlyArray<string>>
54
+ >
55
+
56
+ /**
57
+ * Maps a node kind to the matching visitor callback name. Derived from each
58
+ * definition's `visitorKey`.
59
+ */
60
+ export const VISITOR_KEY_BY_KIND = Object.fromEntries(nodeDefs.flatMap((def) => (def.visitorKey ? [[def.kind, def.visitorKey] as const] : []))) as Partial<
61
+ Record<NodeKind, NonNullable<NodeDef['visitorKey']>>
62
+ >
63
+
64
+ /**
65
+ * Per-kind builders rerun after children are rebuilt. Derived from each
66
+ * definition's `rebuild` flag.
67
+ */
68
+ export const nodeRebuilders = Object.fromEntries(
69
+ nodeDefs.flatMap((def) => (def.rebuild ? [[def.kind, def.create as unknown as (node: Node) => Node] as const] : [])),
70
+ ) as Partial<Record<NodeKind, (node: Node) => Node>>
@@ -1,7 +1,7 @@
1
1
  import { isScalarPrimitive } from './constants.ts'
2
- import { createProperty, createSchema } from './factory.ts'
3
2
  import { narrowSchema } from './guards.ts'
4
- import type { SchemaNode } from './nodes/schema.ts'
3
+ import { createProperty } from './nodes/property.ts'
4
+ import { createSchema, type SchemaNode } from './nodes/schema.ts'
5
5
  import { enumPropName } from './utils/index.ts'
6
6
 
7
7
  /**
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type { DedupeCanonical, DedupeLookups, DedupePlan } from './dedupe.ts'
2
2
  export type { SchemaDialect } from './dialect.ts'
3
- export type { DistributiveOmit } from './factory.ts'
3
+ export type { DistributiveOmit } from './node.ts'
4
4
  export type { InferSchemaNode, ParserOptions } from './infer.ts'
5
5
  export type {
6
6
  ArraySchemaNode,
@@ -20,6 +20,7 @@ export type {
20
20
  HttpMethod,
21
21
  HttpOperationNode,
22
22
  ImportNode,
23
+ IndexedAccessTypeNode,
23
24
  InputMeta,
24
25
  InputNode,
25
26
  IntersectionSchemaNode,
@@ -28,13 +29,12 @@ export type {
28
29
  Node,
29
30
  NodeKind,
30
31
  NumberSchemaNode,
32
+ ObjectBindingPatternNode,
31
33
  ObjectSchemaNode,
32
34
  OperationNode,
33
35
  OutputNode,
34
- ParameterGroupNode,
35
36
  ParameterLocation,
36
37
  ParameterNode,
37
- ParamsTypeNode,
38
38
  PrimitiveSchemaType,
39
39
  PropertyNode,
40
40
  RefSchemaNode,
@@ -48,6 +48,8 @@ export type {
48
48
  StringSchemaNode,
49
49
  TextNode,
50
50
  TimeSchemaNode,
51
+ TypeExpression,
52
+ TypeLiteralNode,
51
53
  TypeNode,
52
54
  UnionSchemaNode,
53
55
  UrlSchemaNode,