@elysiajs/openapi 1.3.11 → 1.4.0-exp.0

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.
@@ -24,6 +24,7 @@ __export(openapi_exports, {
24
24
  getLoosePath: () => getLoosePath,
25
25
  getPossiblePath: () => getPossiblePath,
26
26
  toOpenAPISchema: () => toOpenAPISchema,
27
+ unwrapSchema: () => unwrapSchema,
27
28
  withHeaders: () => withHeaders
28
29
  });
29
30
  module.exports = __toCommonJS(openapi_exports);
@@ -37,6 +38,7 @@ var Hint = Symbol.for("TypeBox.Hint");
37
38
  var Kind = Symbol.for("TypeBox.Kind");
38
39
 
39
40
  // src/openapi.ts
41
+ var import_xsschema = require("xsschema");
40
42
  var capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
41
43
  var toRef = (name) => import_elysia.t.Ref(`#/components/schemas/${name}`);
42
44
  var toOperationId = (method, paths) => {
@@ -65,7 +67,14 @@ var getLoosePath = (path) => {
65
67
  return path.slice(0, path.length - 1);
66
68
  return path + "/";
67
69
  };
68
- function toOpenAPISchema(app, exclude, references) {
70
+ var unwrapSchema = (schema) => {
71
+ if (!schema) return;
72
+ if (typeof schema === "string") schema = toRef(schema);
73
+ if (Kind in schema) return schema;
74
+ if (Kind in schema === false && schema["~standard"])
75
+ return (0, import_xsschema.toJsonSchema)(schema);
76
+ };
77
+ async function toOpenAPISchema(app, exclude, references) {
69
78
  const {
70
79
  methods: excludeMethods = ["OPTIONS"],
71
80
  staticFile: excludeStaticFile = true,
@@ -118,11 +127,11 @@ function toOpenAPISchema(app, exclude, references) {
118
127
  };
119
128
  const parameters = [];
120
129
  if (hooks.params) {
121
- if (typeof hooks.params === "string")
122
- hooks.params = toRef(hooks.params);
123
- if (hooks.params.type === "object" && hooks.params.properties) {
130
+ let params = unwrapSchema(hooks.params);
131
+ if (params) params = await params;
132
+ if (params && params.type === "object" && params.properties)
124
133
  for (const [paramName, paramSchema] of Object.entries(
125
- hooks.params.properties
134
+ params.properties
126
135
  ))
127
136
  parameters.push({
128
137
  name: paramName,
@@ -131,15 +140,14 @@ function toOpenAPISchema(app, exclude, references) {
131
140
  // Path parameters are always required
132
141
  schema: paramSchema
133
142
  });
134
- }
135
143
  }
136
144
  if (hooks.query) {
137
- if (typeof hooks.query === "string")
138
- hooks.query = toRef(hooks.query);
139
- if (hooks.query.type === "object" && hooks.query.properties) {
140
- const required = hooks.query.required || [];
145
+ let query = unwrapSchema(hooks.query);
146
+ if (query) query = await query;
147
+ if (query && query.type === "object" && query.properties) {
148
+ const required = query.required || [];
141
149
  for (const [queryName, querySchema] of Object.entries(
142
- hooks.query.properties
150
+ query.properties
143
151
  ))
144
152
  parameters.push({
145
153
  name: queryName,
@@ -150,12 +158,12 @@ function toOpenAPISchema(app, exclude, references) {
150
158
  }
151
159
  }
152
160
  if (hooks.headers) {
153
- if (typeof hooks.headers === "string")
154
- hooks.headers = toRef(hooks.headers);
155
- if (hooks.headers.type === "object" && hooks.headers.properties) {
156
- const required = hooks.headers.required || [];
161
+ let headers = unwrapSchema(hooks.query);
162
+ if (headers) headers = await headers;
163
+ if (headers && headers.type === "object" && headers.properties) {
164
+ const required = headers.required || [];
157
165
  for (const [headerName, headerSchema] of Object.entries(
158
- hooks.headers.properties
166
+ headers.properties
159
167
  ))
160
168
  parameters.push({
161
169
  name: headerName,
@@ -166,12 +174,12 @@ function toOpenAPISchema(app, exclude, references) {
166
174
  }
167
175
  }
168
176
  if (hooks.cookie) {
169
- if (typeof hooks.cookie === "string")
170
- hooks.cookie = toRef(hooks.cookie);
171
- if (hooks.cookie.type === "object" && hooks.cookie.properties) {
172
- const required = hooks.cookie.required || [];
177
+ let cookie = unwrapSchema(hooks.cookie);
178
+ if (cookie) cookie = await cookie;
179
+ if (cookie && cookie.type === "object" && cookie.properties) {
180
+ const required = cookie.required || [];
173
181
  for (const [cookieName, cookieSchema] of Object.entries(
174
- hooks.cookie.properties
182
+ cookie.properties
175
183
  ))
176
184
  parameters.push({
177
185
  name: cookieName,
@@ -183,80 +191,107 @@ function toOpenAPISchema(app, exclude, references) {
183
191
  }
184
192
  if (parameters.length > 0) operation.parameters = parameters;
185
193
  if (hooks.body && method !== "get" && method !== "head") {
186
- if (typeof hooks.body === "string") hooks.body = toRef(hooks.body);
187
- if (hooks.parse) {
188
- const content = {};
189
- const parsers = hooks.parse;
190
- for (const parser of parsers) {
191
- if (typeof parser.fn === "function") continue;
192
- switch (parser.fn) {
193
- case "text":
194
- case "text/plain":
195
- content["text/plain"] = { schema: hooks.body };
196
- continue;
197
- case "urlencoded":
198
- case "application/x-www-form-urlencoded":
199
- content["application/x-www-form-urlencoded"] = {
200
- schema: hooks.body
201
- };
202
- continue;
203
- case "json":
204
- case "application/json":
205
- content["application/json"] = { schema: hooks.body };
206
- continue;
207
- case "formdata":
208
- case "multipart/form-data":
209
- content["multipart/form-data"] = {
210
- schema: hooks.body
211
- };
212
- continue;
194
+ let body = unwrapSchema(hooks.body);
195
+ if (body) body = await body;
196
+ if (body) {
197
+ const { type: _type, description, ...options } = body;
198
+ const type = _type;
199
+ if (hooks.parse) {
200
+ const content = {};
201
+ const parsers = hooks.parse;
202
+ for (const parser of parsers) {
203
+ if (typeof parser.fn === "function") continue;
204
+ switch (parser.fn) {
205
+ case "text":
206
+ case "text/plain":
207
+ content["text/plain"] = { schema: body };
208
+ continue;
209
+ case "urlencoded":
210
+ case "application/x-www-form-urlencoded":
211
+ content["application/x-www-form-urlencoded"] = {
212
+ schema: body
213
+ };
214
+ continue;
215
+ case "json":
216
+ case "application/json":
217
+ content["application/json"] = { schema: body };
218
+ continue;
219
+ case "formdata":
220
+ case "multipart/form-data":
221
+ content["multipart/form-data"] = {
222
+ schema: body
223
+ };
224
+ continue;
225
+ }
213
226
  }
214
- }
215
- operation.requestBody = { content, required: true };
216
- } else {
217
- operation.requestBody = {
218
- content: {
219
- "application/json": {
220
- schema: hooks.body
221
- },
222
- "application/x-www-form-urlencoded": {
223
- schema: hooks.body
227
+ operation.requestBody = {
228
+ description,
229
+ content,
230
+ required: true
231
+ };
232
+ } else {
233
+ operation.requestBody = {
234
+ description,
235
+ content: type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
236
+ "text/plain": body
237
+ } : {
238
+ "application/json": {
239
+ schema: body
240
+ },
241
+ "application/x-www-form-urlencoded": {
242
+ schema: body
243
+ },
244
+ "multipart/form-data": {
245
+ schema: body
246
+ }
224
247
  },
225
- "multipart/form-data": {
226
- schema: hooks.body
227
- }
228
- },
229
- required: true
230
- };
248
+ required: true
249
+ };
250
+ }
231
251
  }
232
252
  }
233
253
  if (hooks.response) {
234
254
  operation.responses = {};
235
255
  if (typeof hooks.response === "object" && !hooks.response.type && !hooks.response.$ref) {
236
256
  for (let [status, schema] of Object.entries(hooks.response)) {
237
- if (typeof schema === "string") schema = toRef(schema);
238
- const { type, examples, $ref, ...options } = schema;
257
+ let response = unwrapSchema(schema);
258
+ if (response) response = await response;
259
+ if (!response) continue;
260
+ const { type: _type, description, ...options } = response;
261
+ const type = _type;
239
262
  operation.responses[status] = {
240
- description: `Response for status ${status}`,
263
+ description: description ?? `Response for status ${status}`,
241
264
  ...options,
242
- content: type === "void" || type === "null" || type === "undefined" ? schema : {
265
+ content: type === "void" || type === "null" || type === "undefined" ? response : type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
266
+ "text/plain": {
267
+ schema: response
268
+ }
269
+ } : {
243
270
  "application/json": {
244
- schema
271
+ schema: response
245
272
  }
246
273
  }
247
274
  };
248
275
  }
249
276
  } else {
250
- if (typeof hooks.response === "string")
251
- hooks.response = toRef(hooks.response);
252
- operation.responses["200"] = {
253
- description: "Successful response",
254
- content: {
255
- "application/json": {
256
- schema: hooks.response
277
+ let response = unwrapSchema(hooks.response);
278
+ if (response) response = await response;
279
+ if (response) {
280
+ const { type: _type, description, ...options } = response;
281
+ const type = _type;
282
+ operation.responses["200"] = {
283
+ description: description ?? `Response for status 200`,
284
+ content: type === "void" || type === "null" || type === "undefined" ? response : type === "string" || type === "number" || type === "integer" || type === "boolean" ? {
285
+ "text/plain": {
286
+ schema: response
287
+ }
288
+ } : {
289
+ "application/json": {
290
+ schema: response
291
+ }
257
292
  }
258
- }
259
- };
293
+ };
294
+ }
260
295
  }
261
296
  }
262
297
  for (let path of getPossiblePath(route.path)) {
@@ -287,7 +322,14 @@ function toOpenAPISchema(app, exclude, references) {
287
322
  };
288
323
  }
289
324
  }
290
- const schemas = app.getGlobalDefinitions?.().type;
325
+ const _schemas = app.getGlobalDefinitions?.().type;
326
+ const schemas = /* @__PURE__ */ Object.create(null);
327
+ if (_schemas)
328
+ for (const [name, schema] of Object.entries(_schemas)) {
329
+ let jsonSchema = unwrapSchema(schema);
330
+ if (jsonSchema instanceof Promise) jsonSchema = await jsonSchema;
331
+ if (jsonSchema) schemas[name] = jsonSchema;
332
+ }
291
333
  return {
292
334
  components: {
293
335
  schemas
@@ -304,5 +346,6 @@ var withHeaders = (schema, headers) => Object.assign(schema, {
304
346
  getLoosePath,
305
347
  getPossiblePath,
306
348
  toOpenAPISchema,
349
+ unwrapSchema,
307
350
  withHeaders
308
351
  });
@@ -31,6 +31,21 @@ interface OpenAPIGeneratorOptions {
31
31
  * @default false
32
32
  */
33
33
  debug?: boolean;
34
+ /**
35
+ * compilerOptions
36
+ *
37
+ * Override tsconfig.json compilerOptions
38
+ */
39
+ compilerOptions?: Record<string, any>;
40
+ /**
41
+ * Temporary root
42
+ *
43
+ * a folder where temporary files are stored
44
+ * @default os.tmpdir()/.ElysiaAutoOpenAPI
45
+ *
46
+ * ! be careful that the folder will be removed after the process ends
47
+ */
48
+ tmpRoot?: string;
34
49
  }
35
50
  /**
36
51
  * Auto generate OpenAPI schema from Elysia instance
@@ -45,5 +60,5 @@ export declare const fromTypes: (
45
60
  *
46
61
  * The path must export an Elysia instance
47
62
  */
48
- targetFilePath: string, { tsconfigPath, instanceName, projectRoot, overrideOutputPath, debug }?: OpenAPIGeneratorOptions) => () => AdditionalReference | undefined;
63
+ targetFilePath: string, { tsconfigPath, instanceName, projectRoot, overrideOutputPath, debug, compilerOptions, tmpRoot }?: OpenAPIGeneratorOptions) => () => AdditionalReference | undefined;
49
64
  export {};
@@ -19,9 +19,10 @@ var fromTypes = (targetFilePath, {
19
19
  instanceName,
20
20
  projectRoot = process.cwd(),
21
21
  overrideOutputPath,
22
- debug = false
22
+ debug = false,
23
+ compilerOptions,
24
+ tmpRoot = join(tmpdir(), ".ElysiaAutoOpenAPI")
23
25
  } = {}) => () => {
24
- const tmpRoot = join(tmpdir(), ".ElysiaAutoOpenAPI");
25
26
  try {
26
27
  if (!targetFilePath.endsWith(".ts") && !targetFilePath.endsWith(".tsx"))
27
28
  throw new Error("Only .ts files are supported");
@@ -40,25 +41,27 @@ var fromTypes = (targetFilePath, {
40
41
  mkdirSync(tmpRoot, { recursive: true });
41
42
  const tsconfig = tsconfigPath.startsWith("/") ? tsconfigPath : join(projectRoot, tsconfigPath);
42
43
  let extendsRef = existsSync(tsconfig) ? `"extends": "${join(projectRoot, "tsconfig.json")}",` : "";
44
+ let distDir = join(tmpRoot, "dist");
43
45
  if (typeof process !== "undefined" && process.platform === "win32") {
44
46
  extendsRef = extendsRef.replace(/\\/g, "/");
45
47
  src = src.replace(/\\/g, "/");
48
+ distDir = distDir.replace(/\\/g, "/");
46
49
  }
47
50
  writeFileSync(
48
51
  join(tmpRoot, "tsconfig.json"),
49
52
  `{
50
53
  ${extendsRef}
51
- "compilerOptions": {
52
- "lib": ["ESNext"],
53
- "module": "ESNext",
54
- "noEmit": false,
55
- "declaration": true,
56
- "emitDeclarationOnly": true,
57
- "moduleResolution": "bundler",
58
- "skipLibCheck": true,
59
- "skipDefaultLibCheck": true,
60
- "outDir": "./dist"
61
- },
54
+ "compilerOptions": ${compilerOptions ? JSON.stringify(compilerOptions) : `{
55
+ "lib": ["ESNext"],
56
+ "module": "ESNext",
57
+ "noEmit": false,
58
+ "declaration": true,
59
+ "emitDeclarationOnly": true,
60
+ "moduleResolution": "bundler",
61
+ "skipLibCheck": true,
62
+ "skipDefaultLibCheck": true,
63
+ "outDir": "${distDir}"
64
+ }`},
62
65
  "include": ["${src}"]
63
66
  }`
64
67
  );
@@ -100,6 +103,11 @@ var fromTypes = (targetFilePath, {
100
103
  );
101
104
  console.warn(tempFiles);
102
105
  }
106
+ } else {
107
+ console.log(
108
+ "reason: root folder doesn't exists",
109
+ join(tmpRoot, "dist")
110
+ );
103
111
  }
104
112
  return;
105
113
  }
package/dist/index.d.ts CHANGED
@@ -19,16 +19,19 @@ export declare const openapi: <const Enabled extends boolean = true, const Path
19
19
  macro: {};
20
20
  macroFn: {};
21
21
  parser: {};
22
+ response: {};
22
23
  }, {}, {
23
24
  derive: {};
24
25
  resolve: {};
25
26
  schema: {};
26
27
  standaloneSchema: {};
28
+ response: {};
27
29
  }, {
28
30
  derive: {};
29
31
  resolve: {};
30
32
  schema: {};
31
33
  standaloneSchema: {};
34
+ response: {};
32
35
  }>;
33
36
  export { toOpenAPISchema, withHeaders } from './openapi';
34
37
  export type { ElysiaOpenAPIConfig };