File size: 2,949 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { envelopeSchema } from '../protocol/envelope';
import { z } from 'zod';

export function jsonSchema(schema: z.ZodTypeAny): Record<string, unknown> {
  return jsonSchemaForTarget(schema, 'input', 'draft-7');
}

export function outputJsonSchema(schema: z.ZodTypeAny): Record<string, unknown> {
  return jsonSchemaForTarget(schema, 'output', 'draft-7');
}

export function openApiDocumentJsonSchema(
  schema: z.ZodTypeAny,
  io: 'input' | 'output' = 'input',
): Record<string, unknown> {
  return jsonSchemaForTarget(schema, io, 'openapi-3.0');
}

function jsonSchemaForTarget(
  schema: z.ZodTypeAny,
  io: 'input' | 'output',
  target: 'draft-7' | 'openapi-3.0',
): Record<string, unknown> {
  const converted = z.toJSONSchema(schema, {
    target,
    io,
    unrepresentable: 'any',
  }) as Record<string, unknown>;
  if (converted['$schema'] !== undefined) {
    delete converted['$schema'];
  }
  return converted;
}

export function envelopeJsonSchema(
  dataSchema: z.ZodTypeAny,
): Record<string, unknown> {
  return outputJsonSchema(envelopeSchema(dataSchema));
}

export function openApiDocumentEnvelopeJsonSchema(
  dataSchema: z.ZodTypeAny,
): Record<string, unknown> {
  return openApiDocumentJsonSchema(envelopeSchema(dataSchema), 'output');
}

export interface RouteSchemaOptions {
  body?: z.ZodTypeAny;
  querystring?: z.ZodTypeAny;
  params?: z.ZodTypeAny;
  response?: Record<number, z.ZodTypeAny>;
  rawResponse?: Record<number, Record<string, unknown>>;
  description?: string;
  summary?: string;
  tags?: string[];
  operationId?: string;
  consumes?: string[];
  produces?: string[];
}

export function buildRouteSchema(options: RouteSchemaOptions): Record<string, unknown> {
  const schema: Record<string, unknown> = {};

  if (options.body) {
    schema['body'] = jsonSchema(options.body);
  }
  if (options.querystring) {
    schema['querystring'] = jsonSchema(options.querystring);
  }
  if (options.params) {
    schema['params'] = jsonSchema(options.params);
  }
  if (options.response || options.rawResponse) {
    const responses: Record<string, unknown> = {};
    if (options.response) {
      for (const [code, zodSchema] of Object.entries(options.response)) {
        responses[String(code)] = envelopeJsonSchema(zodSchema);
      }
    }
    if (options.rawResponse) {
      for (const [code, rawSchema] of Object.entries(options.rawResponse)) {
        responses[String(code)] = rawSchema;
      }
    }
    schema['response'] = responses;
  }
  if (options.description) {
    schema['description'] = options.description;
  }
  if (options.summary) {
    schema['summary'] = options.summary;
  }
  if (options.tags) {
    schema['tags'] = options.tags;
  }
  if (options.operationId) {
    schema['operationId'] = options.operationId;
  }
  if (options.consumes) {
    schema['consumes'] = options.consumes;
  }
  if (options.produces) {
    schema['produces'] = options.produces;
  }

  return schema;
}