@sdk-it/generic 0.37.1 → 0.39.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.
- package/README.md +117 -0
- package/dist/index.js +116 -11
- package/dist/index.js.map +2 -2
- package/dist/lib/generic.d.ts +4 -1
- package/dist/lib/generic.d.ts.map +1 -1
- package/dist/lib/response-analyzer.d.ts +2 -1
- package/dist/lib/response-analyzer.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -160,6 +160,123 @@ await writeFile('openapi.json', JSON.stringify(spec, null, 2));
|
|
|
160
160
|
|
|
161
161
|
This configuration ensures that any property with the `Decimal` type is represented as a `string` in the generated OpenAPI specification.
|
|
162
162
|
|
|
163
|
+
### Referencing external schemas
|
|
164
|
+
|
|
165
|
+
By default, the analyzer doesn't see beyond the validation schema defined in the validate middleware route handler and expects the schemas to be defined inline.
|
|
166
|
+
|
|
167
|
+
For instance the following route handler is perfectly valid and will be analyzed correctly.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
/**
|
|
171
|
+
* @openapi getAuthor
|
|
172
|
+
* @tags authors
|
|
173
|
+
*/
|
|
174
|
+
app.get(
|
|
175
|
+
'/authors/:id',
|
|
176
|
+
validate((payload) => ({
|
|
177
|
+
id: {
|
|
178
|
+
select: payload.param.id,
|
|
179
|
+
against: z.string(),
|
|
180
|
+
},
|
|
181
|
+
})),
|
|
182
|
+
async (req, res) => {
|
|
183
|
+
const { id } = req.input;
|
|
184
|
+
return res.json({ id, name: 'John Doe' });
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
However, if you want to reference external schemas as shown below, you need to provide a way for the analyzer to resolve the schema.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
// filename: schemas.ts
|
|
193
|
+
import { z } from 'zod';
|
|
194
|
+
|
|
195
|
+
export const authorSchema = z.object({
|
|
196
|
+
id: z.string().uuid(),
|
|
197
|
+
name: z.string().min(2).max(100),
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import crypto from 'node:crypto';
|
|
203
|
+
import { z } from 'zod';
|
|
204
|
+
|
|
205
|
+
import { validate } from '@sdk-it/express/runtime';
|
|
206
|
+
|
|
207
|
+
import { authorSchema } from './schemas';
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @openapi createBook
|
|
211
|
+
* @tags books
|
|
212
|
+
*/
|
|
213
|
+
app.post(
|
|
214
|
+
'/books',
|
|
215
|
+
validate((payload) => ({
|
|
216
|
+
title: {
|
|
217
|
+
select: payload.body.title,
|
|
218
|
+
against: z.string().min(2).max(100),
|
|
219
|
+
},
|
|
220
|
+
author: {
|
|
221
|
+
select: payload.body.author,
|
|
222
|
+
against: authorSchema, // <-- Referencing external schema
|
|
223
|
+
},
|
|
224
|
+
})),
|
|
225
|
+
async (req, res) => {
|
|
226
|
+
const { title, author } = req.input;
|
|
227
|
+
return res.json({ id: crypto.randomUUID(), title, author });
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
In this case the analyzer needs to be able to resolve the `authorSchema` reference to generate the correct OpenAPI schema otherwise it will fail.
|
|
233
|
+
|
|
234
|
+
Luckily, the analyzer provides an `imports` option that allows you to specify additional files to be included in the analysis.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { join } from 'node:path';
|
|
238
|
+
|
|
239
|
+
import { analyze } from '@sdk-it/generic';
|
|
240
|
+
|
|
241
|
+
const { paths, components } = await analyze('path/to/tsconfig.json', {
|
|
242
|
+
responseAnalyzer,
|
|
243
|
+
imports: [
|
|
244
|
+
{
|
|
245
|
+
import: 'schemas',
|
|
246
|
+
from: join(process.cwd(), 'path/to/schemas.ts'), // <-- Path to the file containing the external schema
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Now you need to update the import to namespace imports in the route handler where the `schemas` variable is used.
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import * as schemas from './schemas';
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @openapi createBook
|
|
259
|
+
* @tags books
|
|
260
|
+
*/
|
|
261
|
+
app.post(
|
|
262
|
+
'/books',
|
|
263
|
+
validate((payload) => ({
|
|
264
|
+
title: {
|
|
265
|
+
select: payload.body.title,
|
|
266
|
+
against: z.string().min(2).max(100),
|
|
267
|
+
},
|
|
268
|
+
author: {
|
|
269
|
+
select: payload.body.author,
|
|
270
|
+
against: schemas.authorSchema,
|
|
271
|
+
},
|
|
272
|
+
})),
|
|
273
|
+
async (req, res) => {
|
|
274
|
+
const { title, author } = req.input;
|
|
275
|
+
return res.json({ id: crypto.randomUUID(), title, author });
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
```
|
|
279
|
+
|
|
163
280
|
### Control endpoint/operation visibility
|
|
164
281
|
|
|
165
282
|
You can control the visibility of endpoints and operations in the generated OpenAPI specification by using the `@access` tag in your JSDoc comments. for now only `private` is supported.
|
package/dist/index.js
CHANGED
|
@@ -10,9 +10,36 @@ import {
|
|
|
10
10
|
isHttpMethod,
|
|
11
11
|
toSchema
|
|
12
12
|
} from "@sdk-it/core";
|
|
13
|
-
|
|
13
|
+
function symbolFile(symbol) {
|
|
14
|
+
if (!symbol) {
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
17
|
+
const declarations = symbol.declarations ?? [];
|
|
18
|
+
if (declarations.length === 0) {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
const sourceFile = declarations[0].getSourceFile();
|
|
22
|
+
return sourceFile?.fileName;
|
|
23
|
+
}
|
|
24
|
+
function isExternalFunction(symbol) {
|
|
25
|
+
const fileName = symbolFile(symbol);
|
|
26
|
+
return fileName ? fileName.includes("node_modules") : false;
|
|
27
|
+
}
|
|
28
|
+
function isLocalFunction(symbol) {
|
|
29
|
+
if (!symbol) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return !isExternalFunction(symbol);
|
|
33
|
+
}
|
|
34
|
+
var returnTokens = (node, typeChecker, options) => {
|
|
14
35
|
const tokens = [];
|
|
15
|
-
const
|
|
36
|
+
const consider3rdParty = options?.consider3rdParty ?? false;
|
|
37
|
+
const maxDepth = options?.maxDepth ?? 5;
|
|
38
|
+
const visitedFunctions = /* @__PURE__ */ new Set();
|
|
39
|
+
const visitor = (node2, depth) => {
|
|
40
|
+
if (depth > maxDepth) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
16
43
|
if (ts.isThrowStatement(node2)) {
|
|
17
44
|
if (ts.isNewExpression(node2.expression)) {
|
|
18
45
|
tokens.push({
|
|
@@ -28,11 +55,39 @@ var returnToken = (node) => {
|
|
|
28
55
|
node: node2.expression
|
|
29
56
|
});
|
|
30
57
|
}
|
|
31
|
-
|
|
58
|
+
ts.forEachChild(node2.expression, (child) => visitor(child, depth));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (ts.isCallExpression(node2) && typeChecker && depth < maxDepth) {
|
|
62
|
+
const callExpression = node2;
|
|
63
|
+
let symbol;
|
|
64
|
+
if (ts.isIdentifier(callExpression.expression)) {
|
|
65
|
+
symbol = typeChecker.getSymbolAtLocation(callExpression.expression);
|
|
66
|
+
} else if (ts.isPropertyAccessExpression(callExpression.expression)) {
|
|
67
|
+
symbol = typeChecker.getSymbolAtLocation(
|
|
68
|
+
callExpression.expression.name
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
|
|
72
|
+
symbol = typeChecker.getAliasedSymbol(symbol);
|
|
73
|
+
}
|
|
74
|
+
const shouldFollow = consider3rdParty || isLocalFunction(symbol);
|
|
75
|
+
if (shouldFollow && symbol) {
|
|
76
|
+
const declarations = symbol?.declarations ?? [];
|
|
77
|
+
for (const declaration of declarations) {
|
|
78
|
+
if (visitedFunctions.has(declaration)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (isFunctionWithBody(declaration) && declaration.body) {
|
|
82
|
+
visitedFunctions.add(declaration);
|
|
83
|
+
visitor(declaration.body, depth + 1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
32
87
|
}
|
|
33
|
-
|
|
88
|
+
ts.forEachChild(node2, (child) => visitor(child, depth));
|
|
34
89
|
};
|
|
35
|
-
|
|
90
|
+
visitor(node, 0);
|
|
36
91
|
return tokens;
|
|
37
92
|
};
|
|
38
93
|
var logger = debug("@sdk-it/generic");
|
|
@@ -94,7 +149,7 @@ function parseJSDocComment(node) {
|
|
|
94
149
|
summary
|
|
95
150
|
};
|
|
96
151
|
}
|
|
97
|
-
function visit(node, responseAnalyzer2, paths) {
|
|
152
|
+
function visit(node, responseAnalyzer2, paths, typeChecker) {
|
|
98
153
|
if (!ts.isCallExpression(node) || node.arguments.length < 2) {
|
|
99
154
|
return moveOn();
|
|
100
155
|
}
|
|
@@ -139,13 +194,53 @@ function visit(node, responseAnalyzer2, paths) {
|
|
|
139
194
|
if (!selector || !ts.isParenthesizedExpression(selector.body) || !ts.isObjectLiteralExpression(selector.body.expression)) {
|
|
140
195
|
return moveOn();
|
|
141
196
|
}
|
|
197
|
+
const middlewareDeclarations = [];
|
|
198
|
+
for (const arg of node.arguments.slice(1, -1)) {
|
|
199
|
+
if (ts.isCallExpression(arg)) {
|
|
200
|
+
if (ts.isIdentifier(arg.expression)) {
|
|
201
|
+
let symbol = typeChecker.getSymbolAtLocation(arg.expression);
|
|
202
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
|
|
203
|
+
symbol = typeChecker.getAliasedSymbol(symbol);
|
|
204
|
+
}
|
|
205
|
+
const allDeclarations = [
|
|
206
|
+
symbol?.valueDeclaration,
|
|
207
|
+
...symbol?.declarations ?? []
|
|
208
|
+
].filter((it) => !!it);
|
|
209
|
+
let declaration = allDeclarations.find(isFunctionWithBody);
|
|
210
|
+
if (!declaration) {
|
|
211
|
+
for (const decl of allDeclarations) {
|
|
212
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
213
|
+
if (isFunctionWithBody(decl.initializer)) {
|
|
214
|
+
declaration = decl.initializer;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (declaration) {
|
|
221
|
+
middlewareDeclarations.push(declaration);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (ts.isIdentifier(arg.expression) && arg.expression.text === "validate") {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const firstArg = arg.arguments[0];
|
|
228
|
+
if (isFunctionWithBody(firstArg)) {
|
|
229
|
+
middlewareDeclarations.push(firstArg);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
142
233
|
const props = selector.body.expression.properties.filter(
|
|
143
234
|
ts.isPropertyAssignment
|
|
144
235
|
);
|
|
145
236
|
const sourceFile = node.getSourceFile();
|
|
146
|
-
const tokens = returnToken(handler);
|
|
147
237
|
const responses = [];
|
|
148
|
-
for (const
|
|
238
|
+
for (const middlewareDecl of middlewareDeclarations) {
|
|
239
|
+
for (const { token, node: node2 } of returnTokens(middlewareDecl, typeChecker)) {
|
|
240
|
+
responses.push(...responseAnalyzer2(middlewareDecl, token, node2));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (const { token, node: node2 } of returnTokens(handler, typeChecker)) {
|
|
149
244
|
responses.push(...responseAnalyzer2(handler, token, node2));
|
|
150
245
|
}
|
|
151
246
|
paths.addPath(
|
|
@@ -159,7 +254,10 @@ function visit(node, responseAnalyzer2, paths) {
|
|
|
159
254
|
metadata
|
|
160
255
|
);
|
|
161
256
|
function moveOn() {
|
|
162
|
-
ts.forEachChild(
|
|
257
|
+
ts.forEachChild(
|
|
258
|
+
node,
|
|
259
|
+
(node2) => visit(node2, responseAnalyzer2, paths, typeChecker)
|
|
260
|
+
);
|
|
163
261
|
}
|
|
164
262
|
}
|
|
165
263
|
function toSelectors(props) {
|
|
@@ -216,7 +314,8 @@ async function analyze(tsconfigPath, config) {
|
|
|
216
314
|
}
|
|
217
315
|
return responseAnalyzer2(handler, typeDeriver);
|
|
218
316
|
},
|
|
219
|
-
paths
|
|
317
|
+
paths,
|
|
318
|
+
typeChecker
|
|
220
319
|
);
|
|
221
320
|
}
|
|
222
321
|
}
|
|
@@ -232,6 +331,12 @@ async function analyze(tsconfigPath, config) {
|
|
|
232
331
|
components
|
|
233
332
|
};
|
|
234
333
|
}
|
|
334
|
+
function isFunctionWithBody(node) {
|
|
335
|
+
if (!node) {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
return (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node)) && !!node.body;
|
|
339
|
+
}
|
|
235
340
|
|
|
236
341
|
// packages/generic/src/lib/response-analyzer.ts
|
|
237
342
|
import ts2 from "typescript";
|
|
@@ -322,6 +427,6 @@ export {
|
|
|
322
427
|
analyze,
|
|
323
428
|
defaultResponseAnalyzer,
|
|
324
429
|
responseAnalyzer,
|
|
325
|
-
|
|
430
|
+
returnTokens
|
|
326
431
|
};
|
|
327
432
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/lib/generic.ts", "../src/lib/response-analyzer.ts"],
|
|
4
|
-
"sourcesContent": ["import debug from 'debug';\nimport type { ComponentsObject } from 'openapi3-ts/oas31';\nimport { camelcase } from 'stringcase';\nimport ts from 'typescript';\n\nimport {\n type InjectImport,\n type NaunceResponseAnalyzer,\n type OnOperation,\n Paths,\n type ResponseAnalyzerFn,\n type ResponseItem,\n type Selector,\n type SemanticSource,\n TypeDeriver,\n getProgram,\n isCallExpression,\n isHttpMethod,\n toSchema,\n} from '@sdk-it/core';\n\nexport const returnToken = (node: ts.ArrowFunction) => {\n const tokens: { token: string; node: ts.Expression }[] = [];\n\n const visitor: ts.Visitor = (node) => {\n if (ts.isThrowStatement(node)) {\n if (ts.isNewExpression(node.expression)) {\n tokens.push({\n token: `throw.new.${node.expression.expression.getText()}`,\n node: node.expression,\n });\n }\n }\n\n if (ts.isReturnStatement(node) && node.expression) {\n if (ts.isCallExpression(node.expression)) {\n tokens.push({\n token: node.expression.expression.getText(),\n node: node.expression,\n });\n }\n return undefined;\n }\n\n return ts.forEachChild(node, visitor);\n };\n\n ts.forEachChild(node, visitor);\n return tokens;\n};\n\nconst logger = debug('@sdk-it/generic');\n\nconst jsDocsTags = [\n 'openapi',\n 'tags',\n 'description',\n 'summary',\n 'access',\n 'tool',\n 'toolDescription',\n] as const;\ntype JSDocsTags = (typeof jsDocsTags)[number];\n\nfunction parseJSDocComment(node: ts.Node) {\n let tags: string[] = [];\n let name = '';\n let description = '';\n let summary = '';\n let access = '';\n let tool = '';\n let toolDescription = '';\n\n for (const tag of ts.getAllJSDocTags(node, (tag): tag is ts.JSDocTag =>\n jsDocsTags.includes(tag.tagName.text as JSDocsTags),\n )) {\n if (typeof tag.comment !== 'string') {\n continue;\n }\n switch (tag.tagName.text as JSDocsTags) {\n case 'openapi':\n name = tag.comment;\n break;\n case 'tags':\n tags = tag.comment.split(',').map((tag) => tag.trim());\n break;\n case 'description':\n description = tag.comment;\n break;\n case 'summary':\n summary = tag.comment;\n break;\n case 'access':\n access = tag.comment.trim().toLowerCase();\n break;\n case 'tool':\n tool = tag.comment.trim();\n break;\n case 'toolDescription':\n toolDescription = tag.comment.trim();\n break;\n }\n }\n return {\n name,\n tags,\n description,\n access,\n tool,\n toolDescription,\n summary,\n };\n}\n\nfunction visit(\n node: ts.Node,\n responseAnalyzer: (\n handler: ts.ArrowFunction,\n token: string,\n node: ts.Node,\n ) => ResponseItem[],\n paths: Paths,\n) {\n if (!ts.isCallExpression(node) || node.arguments.length < 2) {\n return moveOn();\n }\n if (\n !ts.isPropertyAccessExpression(node.expression) ||\n !ts.isIdentifier(node.expression.name) ||\n !isHttpMethod(node.expression.name.text)\n ) {\n return moveOn();\n }\n\n const [pathNode] = node.arguments;\n if (!ts.isStringLiteral(pathNode)) {\n return moveOn();\n }\n const method = node.expression.name.text;\n const path = pathNode.text;\n const validate = node.arguments.find((arg) =>\n isCallExpression(arg, 'validate'),\n );\n if (!validate) {\n return moveOn();\n }\n const handler = node.arguments.at(-1);\n if (!handler || !ts.isArrowFunction(handler)) {\n return moveOn();\n }\n\n const metadata = parseJSDocComment(node.parent);\n // Skip endpoints marked as private access\n if (metadata.access === 'private') {\n return moveOn();\n }\n const operationName =\n metadata.name ||\n camelcase(`${method} ${path.replace(/[^a-zA-Z0-9]/g, '')}`);\n if (!validate.arguments.length) {\n return moveOn();\n }\n let selector: ts.Expression | undefined;\n let contentType: ts.Expression | undefined;\n if (validate.arguments.length === 2) {\n contentType = validate.arguments[0];\n selector = validate.arguments[1];\n } else {\n selector = validate.arguments[0];\n }\n if (!ts.isArrowFunction(selector)) {\n return moveOn();\n }\n if (\n !selector ||\n !ts.isParenthesizedExpression(selector.body) ||\n !ts.isObjectLiteralExpression(selector.body.expression)\n ) {\n return moveOn();\n }\n const props = selector.body.expression.properties.filter(\n ts.isPropertyAssignment,\n );\n\n const sourceFile = node.getSourceFile();\n const tokens = returnToken(handler);\n\n const responses: ResponseItem[] = [];\n for (const { token, node } of tokens) {\n responses.push(...responseAnalyzer(handler, token, node));\n }\n\n paths.addPath(\n operationName,\n path,\n method,\n contentType\n ? ts.isStringLiteral(contentType)\n ? contentType.text\n : undefined\n : undefined,\n toSelectors(props),\n responses,\n sourceFile.fileName,\n metadata,\n );\n\n function moveOn() {\n ts.forEachChild(node, (node) => visit(node, responseAnalyzer, paths));\n }\n}\n\nfunction toSelectors(props: ts.PropertyAssignment[]) {\n const selectors: Selector[] = [];\n for (const prop of props) {\n if (!ts.isObjectLiteralExpression(prop.initializer)) {\n continue;\n }\n const name = prop.name.getText();\n const select = prop.initializer.properties\n .filter(ts.isPropertyAssignment)\n .find((prop) => prop.name.getText() === 'select');\n if (!select) {\n console.warn(`No select found in ${name}`);\n continue;\n }\n const against = prop.initializer.properties\n .filter(ts.isPropertyAssignment)\n .find((prop) => prop.name.getText() === 'against');\n if (!against) {\n console.warn(`No against found in ${name}`);\n continue;\n }\n const [, source, selectText] = select.initializer.getText().split('.');\n selectors.push({\n name: selectText,\n against: against.initializer.getText(),\n source: source as SemanticSource,\n });\n }\n return selectors;\n}\n\nexport async function analyze(\n tsconfigPath: string,\n config: {\n /**\n * Additional code to inject before resolving zod schemas\n */\n imports?: InjectImport[];\n typesMap?: Record<string, string>;\n responseAnalyzer: ResponseAnalyzerFn | NaunceResponseAnalyzer;\n onOperation?: OnOperation;\n },\n) {\n logger(`Parsing tsconfig`);\n const program = getProgram(tsconfigPath);\n logger(`Program created`);\n const typeChecker = program.getTypeChecker();\n\n logger(`Type checker created`);\n const typeDeriver = new TypeDeriver(typeChecker, config.typesMap);\n const paths = new Paths({\n imports: config.imports ?? [],\n onOperation: config.onOperation,\n });\n\n for (const sourceFile of program.getSourceFiles()) {\n logger(`Analyzing ${sourceFile.fileName}`);\n if (!sourceFile.isDeclarationFile) {\n logger(`Visiting ${sourceFile.fileName}`);\n visit(\n sourceFile,\n (handler, token, node) => {\n const responseAnalyzer = config.responseAnalyzer;\n if (typeof responseAnalyzer !== 'function') {\n const naunce =\n responseAnalyzer[token] || responseAnalyzer['default'];\n if (!naunce) {\n throw new Error(`No response analyzer for token ${token}`);\n }\n return naunce(handler, typeDeriver, node);\n }\n return responseAnalyzer(handler, typeDeriver);\n },\n paths,\n );\n }\n }\n\n const components: ComponentsObject = {\n schemas: Object.entries(typeDeriver.collector).reduce(\n (acc, [key, value]) => ({ ...acc, [key]: toSchema(value) }),\n {},\n ),\n };\n\n return {\n paths: await paths.getPaths(),\n tags: paths.getTags(),\n components,\n };\n}\n\nexport type Serialized = ReturnType<typeof analyze>;\n", "import ts from 'typescript';\n\nimport type {\n NaunceResponseAnalyzer,\n ResponseItem,\n TypeDeriver,\n} from '@sdk-it/core';\n\nconst handlerVisitor: (\n on: (\n node: ts.Node | undefined,\n statusCode: ts.Node | undefined,\n headers: ts.Node | undefined,\n contentType: string,\n ) => void,\n) => ts.Visitor = (callback) => {\n return (node: ts.Node) => {\n if (ts.isReturnStatement(node) && node.expression) {\n if (\n ts.isCallExpression(node.expression) &&\n ts.isPropertyAccessExpression(node.expression.expression)\n ) {\n const propAccess = node.expression.expression;\n if (\n ts.isIdentifier(propAccess.expression) &&\n propAccess.expression.text === 'output'\n ) {\n let contentType = 'application/json';\n const callerMethod = propAccess.name.text;\n const [body, statusCode, headers] = node.expression.arguments;\n if (callerMethod === 'attachment') {\n contentType = 'application/octet-stream';\n }\n if (!body) {\n contentType = 'empty';\n }\n callback(body, statusCode, headers, contentType);\n }\n }\n }\n return ts.forEachChild(node, handlerVisitor(callback));\n };\n};\n\nfunction toResponses(handler: ts.ArrowFunction, deriver: TypeDeriver) {\n const responsesList: ResponseItem[] = [];\n const visit = handlerVisitor((node, statusCode, headers, contentType) => {\n responsesList.push({\n headers: headers ? Object.keys(deriver.serializeNode(headers)) : [],\n contentType,\n statusCode: statusCode ? resolveStatusCode(statusCode) : '200',\n response: node ? deriver.serializeNode(node) : undefined,\n });\n });\n visit(handler.body);\n return responsesList;\n}\n\nfunction resolveStatusCode(node: ts.Node) {\n if (ts.isNumericLiteral(node)) {\n return node.text;\n }\n throw new Error(`Could not resolve status code`);\n}\n\nexport function defaultResponseAnalyzer(\n handler: ts.ArrowFunction,\n deriver: TypeDeriver,\n) {\n try {\n return toResponses(handler, deriver);\n } catch (error) {\n console.error('Error analyzing response\\n', handler.getText());\n throw error;\n }\n}\n\nexport const responseAnalyzer: NaunceResponseAnalyzer = {\n 'throw.new.ProblemDetailsException': (handler, deriver, node) => {\n if (ts.isNewExpression(node)) {\n const [problem] = node.arguments ?? [];\n if (!ts.isObjectLiteralExpression(problem)) {\n return [];\n }\n const properties = problem.properties.reduce<Record<string, string>>(\n (acc, prop) => {\n if (ts.isPropertyAssignment(prop)) {\n const key = prop.name.getText();\n if (ts.isLiteralExpression(prop.initializer)) {\n acc[key] = prop.initializer.text;\n } else {\n acc[key] = prop.initializer.getText();\n }\n }\n return acc;\n },\n {},\n );\n return [\n {\n contentType: 'application/problem+json',\n headers: [],\n statusCode: properties.status,\n response: problem ? deriver.serializeNode(problem) : undefined,\n },\n ];\n }\n return [];\n },\n default: defaultResponseAnalyzer,\n};\n"],
|
|
5
|
-
"mappings": ";AAAA,OAAO,WAAW;AAElB,SAAS,iBAAiB;AAC1B,OAAO,QAAQ;AAEf;AAAA,EAIE;AAAA,EAKA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,
|
|
4
|
+
"sourcesContent": ["import debug from 'debug';\nimport type { ComponentsObject } from 'openapi3-ts/oas31';\nimport { camelcase } from 'stringcase';\nimport ts from 'typescript';\n\nimport {\n type InjectImport,\n type NaunceResponseAnalyzer,\n type OnOperation,\n Paths,\n type ResponseAnalyzerFn,\n type ResponseItem,\n type Selector,\n type SemanticSource,\n TypeDeriver,\n getProgram,\n isCallExpression,\n isHttpMethod,\n toSchema,\n} from '@sdk-it/core';\n\n/**\n * Gets the file path from a symbol's first declaration\n */\nfunction symbolFile(symbol: ts.Symbol | undefined): string | undefined {\n if (!symbol) {\n return undefined;\n }\n\n const declarations = symbol.declarations ?? [];\n if (declarations.length === 0) {\n return undefined;\n }\n\n const sourceFile = declarations[0].getSourceFile();\n return sourceFile?.fileName;\n}\n\n/**\n * Determines if a symbol is from an external library (node_modules)\n */\nfunction isExternalFunction(symbol: ts.Symbol | undefined): boolean {\n const fileName = symbolFile(symbol);\n return fileName ? fileName.includes('node_modules') : false;\n}\n\n/**\n * Determines if a symbol refers to a local function (not from node_modules)\n */\nfunction isLocalFunction(symbol: ts.Symbol | undefined): boolean {\n if (!symbol) {\n return false;\n }\n\n return !isExternalFunction(symbol);\n}\n\nexport const returnTokens = (\n node: ts.Node,\n typeChecker?: ts.TypeChecker,\n options?: { consider3rdParty?: boolean; maxDepth?: number },\n) => {\n const tokens: { token: string; node: ts.Expression }[] = [];\n const consider3rdParty = options?.consider3rdParty ?? false;\n const maxDepth = options?.maxDepth ?? 5;\n // Track visited function declarations to prevent infinite recursion\n const visitedFunctions = new Set<ts.Declaration>();\n\n const visitor = (node: ts.Node, depth: number): void => {\n // Skip if we've exceeded max depth\n if (depth > maxDepth) {\n return;\n }\n\n if (ts.isThrowStatement(node)) {\n if (ts.isNewExpression(node.expression)) {\n tokens.push({\n token: `throw.new.${node.expression.expression.getText()}`,\n node: node.expression,\n });\n }\n }\n\n if (ts.isReturnStatement(node) && node.expression) {\n if (ts.isCallExpression(node.expression)) {\n tokens.push({\n token: node.expression.expression.getText(),\n node: node.expression,\n });\n }\n // Continue traversing into the returned expression (e.g., arrow functions)\n // This handles: return async (c, next) => { throw ... }\n ts.forEachChild(node.expression, (child) => visitor(child, depth));\n return;\n }\n\n // If we encounter a call expression and have a type checker, follow it\n if (ts.isCallExpression(node) && typeChecker && depth < maxDepth) {\n const callExpression = node;\n\n // Try to resolve the function being called\n let symbol: ts.Symbol | undefined;\n\n if (ts.isIdentifier(callExpression.expression)) {\n symbol = typeChecker.getSymbolAtLocation(callExpression.expression);\n } else if (ts.isPropertyAccessExpression(callExpression.expression)) {\n symbol = typeChecker.getSymbolAtLocation(\n callExpression.expression.name,\n );\n }\n\n // Resolve aliases\n if (symbol && symbol.flags & ts.SymbolFlags.Alias) {\n symbol = typeChecker.getAliasedSymbol(symbol);\n }\n\n // Check if we should follow this function\n const shouldFollow = consider3rdParty || isLocalFunction(symbol);\n\n if (shouldFollow && symbol) {\n const declarations = symbol?.declarations ?? [];\n\n for (const declaration of declarations) {\n // Skip if we've already visited this function (prevent infinite recursion)\n if (visitedFunctions.has(declaration)) {\n continue;\n }\n\n if (isFunctionWithBody(declaration) && declaration.body) {\n visitedFunctions.add(declaration);\n // Recursively visit the function body with incremented depth\n visitor(declaration.body, depth + 1);\n }\n }\n }\n }\n\n ts.forEachChild(node, (child) => visitor(child, depth));\n };\n\n visitor(node, 0);\n return tokens;\n};\n\nconst logger = debug('@sdk-it/generic');\n\nconst jsDocsTags = [\n 'openapi',\n 'tags',\n 'description',\n 'summary',\n 'access',\n 'tool',\n 'toolDescription',\n] as const;\ntype JSDocsTags = (typeof jsDocsTags)[number];\n\nfunction parseJSDocComment(node: ts.Node) {\n let tags: string[] = [];\n let name = '';\n let description = '';\n let summary = '';\n let access = '';\n let tool = '';\n let toolDescription = '';\n\n for (const tag of ts.getAllJSDocTags(node, (tag): tag is ts.JSDocTag =>\n jsDocsTags.includes(tag.tagName.text as JSDocsTags),\n )) {\n if (typeof tag.comment !== 'string') {\n continue;\n }\n switch (tag.tagName.text as JSDocsTags) {\n case 'openapi':\n name = tag.comment;\n break;\n case 'tags':\n tags = tag.comment.split(',').map((tag) => tag.trim());\n break;\n case 'description':\n description = tag.comment;\n break;\n case 'summary':\n summary = tag.comment;\n break;\n case 'access':\n access = tag.comment.trim().toLowerCase();\n break;\n case 'tool':\n tool = tag.comment.trim();\n break;\n case 'toolDescription':\n toolDescription = tag.comment.trim();\n break;\n }\n }\n return {\n name,\n tags,\n description,\n access,\n tool,\n toolDescription,\n summary,\n };\n}\n\nfunction visit(\n node: ts.Node,\n responseAnalyzer: (\n handler: ts.ArrowFunction | ts.FunctionExpression,\n token: string,\n node: ts.Node,\n ) => ResponseItem[],\n paths: Paths,\n typeChecker: ts.TypeChecker,\n) {\n if (!ts.isCallExpression(node) || node.arguments.length < 2) {\n return moveOn();\n }\n if (\n !ts.isPropertyAccessExpression(node.expression) ||\n !ts.isIdentifier(node.expression.name) ||\n !isHttpMethod(node.expression.name.text)\n ) {\n return moveOn();\n }\n\n const [pathNode] = node.arguments;\n if (!ts.isStringLiteral(pathNode)) {\n return moveOn();\n }\n const method = node.expression.name.text;\n const path = pathNode.text;\n const validate = node.arguments.find((arg) =>\n isCallExpression(arg, 'validate'),\n );\n if (!validate) {\n return moveOn();\n }\n const handler = node.arguments.at(-1);\n if (!handler || !ts.isArrowFunction(handler)) {\n return moveOn();\n }\n const metadata = parseJSDocComment(node.parent);\n // Skip endpoints marked as private access\n if (metadata.access === 'private') {\n return moveOn();\n }\n const operationName =\n metadata.name ||\n camelcase(`${method} ${path.replace(/[^a-zA-Z0-9]/g, '')}`);\n if (!validate.arguments.length) {\n return moveOn();\n }\n\n let selector: ts.Expression | undefined;\n let contentType: ts.Expression | undefined;\n if (validate.arguments.length === 2) {\n contentType = validate.arguments[0];\n selector = validate.arguments[1];\n } else {\n selector = validate.arguments[0];\n }\n if (!ts.isArrowFunction(selector)) {\n return moveOn();\n }\n if (\n !selector ||\n !ts.isParenthesizedExpression(selector.body) ||\n !ts.isObjectLiteralExpression(selector.body.expression)\n ) {\n return moveOn();\n }\n\n // Collect all middleware declarations for analysis\n const middlewareDeclarations: ts.Node[] = [];\n\n // slice(1, -1) to skip first (path) and last (handler) arguments\n // and skip the validate middleware - it's handled separately\n for (const arg of node.arguments.slice(1, -1)) {\n if (ts.isCallExpression(arg)) {\n // Try to resolve the factory function declaration\n if (ts.isIdentifier(arg.expression)) {\n let symbol = typeChecker.getSymbolAtLocation(arg.expression);\n\n // If symbol has alias, resolve to the actual symbol\n if (symbol && symbol.flags & ts.SymbolFlags.Alias) {\n symbol = typeChecker.getAliasedSymbol(symbol);\n }\n\n const allDeclarations = [\n symbol?.valueDeclaration,\n ...(symbol?.declarations ?? []),\n ].filter((it) => !!it);\n\n let declaration = allDeclarations.find(isFunctionWithBody);\n\n // If not found, check for variable declarations with function initializers\n if (!declaration) {\n for (const decl of allDeclarations) {\n if (ts.isVariableDeclaration(decl) && decl.initializer) {\n if (isFunctionWithBody(decl.initializer)) {\n declaration = decl.initializer;\n break;\n }\n }\n }\n }\n\n if (declaration) {\n middlewareDeclarations.push(declaration);\n }\n }\n\n // Also check if the call expression argument itself is an arrow function\n // e.g., middleware((ctx) => {...})\n // But skip if the function name is 'validate'\n if (\n ts.isIdentifier(arg.expression) &&\n arg.expression.text === 'validate'\n ) {\n continue;\n }\n const firstArg = arg.arguments[0];\n if (isFunctionWithBody(firstArg)) {\n middlewareDeclarations.push(firstArg);\n }\n }\n }\n\n const props = selector.body.expression.properties.filter(\n ts.isPropertyAssignment,\n );\n\n const sourceFile = node.getSourceFile();\n\n const responses: ResponseItem[] = [];\n\n // Analyze all middlewares for potential responses\n for (const middlewareDecl of middlewareDeclarations) {\n for (const { token, node } of returnTokens(middlewareDecl, typeChecker)) {\n responses.push(...responseAnalyzer(middlewareDecl as any, token, node));\n }\n }\n\n // Analyze the main handler for responses\n for (const { token, node } of returnTokens(handler, typeChecker)) {\n responses.push(...responseAnalyzer(handler, token, node));\n }\n\n paths.addPath(\n operationName,\n path,\n method,\n contentType\n ? ts.isStringLiteral(contentType)\n ? contentType.text\n : undefined\n : undefined,\n toSelectors(props),\n responses,\n sourceFile.fileName,\n metadata,\n );\n\n function moveOn() {\n ts.forEachChild(node, (node) =>\n visit(node, responseAnalyzer, paths, typeChecker),\n );\n }\n}\n\nfunction toSelectors(props: ts.PropertyAssignment[]) {\n const selectors: Selector[] = [];\n for (const prop of props) {\n if (!ts.isObjectLiteralExpression(prop.initializer)) {\n continue;\n }\n const name = prop.name.getText();\n const select = prop.initializer.properties\n .filter(ts.isPropertyAssignment)\n .find((prop) => prop.name.getText() === 'select');\n if (!select) {\n console.warn(`No select found in ${name}`);\n continue;\n }\n const against = prop.initializer.properties\n .filter(ts.isPropertyAssignment)\n .find((prop) => prop.name.getText() === 'against');\n if (!against) {\n console.warn(`No against found in ${name}`);\n continue;\n }\n const [, source, selectText] = select.initializer.getText().split('.');\n selectors.push({\n name: selectText,\n against: against.initializer.getText(),\n source: source as SemanticSource,\n });\n }\n return selectors;\n}\n\nexport async function analyze(\n tsconfigPath: string,\n config: {\n /**\n * Additional code to inject before resolving zod schemas\n */\n imports?: InjectImport[];\n typesMap?: Record<string, string>;\n responseAnalyzer: ResponseAnalyzerFn | NaunceResponseAnalyzer;\n onOperation?: OnOperation;\n },\n) {\n logger(`Parsing tsconfig`);\n const program = getProgram(tsconfigPath);\n logger(`Program created`);\n const typeChecker = program.getTypeChecker();\n\n logger(`Type checker created`);\n const typeDeriver = new TypeDeriver(typeChecker, config.typesMap);\n const paths = new Paths({\n imports: config.imports ?? [],\n onOperation: config.onOperation,\n });\n\n for (const sourceFile of program.getSourceFiles()) {\n logger(`Analyzing ${sourceFile.fileName}`);\n if (!sourceFile.isDeclarationFile) {\n logger(`Visiting ${sourceFile.fileName}`);\n visit(\n sourceFile,\n (handler, token, node) => {\n const responseAnalyzer = config.responseAnalyzer;\n if (typeof responseAnalyzer !== 'function') {\n const naunce =\n responseAnalyzer[token] || responseAnalyzer['default'];\n if (!naunce) {\n throw new Error(`No response analyzer for token ${token}`);\n }\n return naunce(handler, typeDeriver, node);\n }\n return responseAnalyzer(handler, typeDeriver);\n },\n paths,\n typeChecker,\n );\n }\n }\n\n const components: ComponentsObject = {\n schemas: Object.entries(typeDeriver.collector).reduce(\n (acc, [key, value]) => ({ ...acc, [key]: toSchema(value) }),\n {},\n ),\n };\n\n return {\n paths: await paths.getPaths(),\n tags: paths.getTags(),\n components,\n };\n}\n\nexport type Serialized = ReturnType<typeof analyze>;\n\nfunction isFunctionWithBody(\n node: ts.Node | ts.Declaration | undefined,\n): node is ts.FunctionLikeDeclaration & { body: ts.Block | ts.Expression } {\n if (!node) {\n return false;\n }\n return (\n (ts.isFunctionDeclaration(node) ||\n ts.isFunctionExpression(node) ||\n ts.isArrowFunction(node) ||\n ts.isMethodDeclaration(node) ||\n ts.isConstructorDeclaration(node) ||\n ts.isGetAccessor(node) ||\n ts.isSetAccessor(node)) &&\n !!node.body\n );\n}\n", "import ts from 'typescript';\n\nimport type {\n NaunceResponseAnalyzer,\n ResponseItem,\n TypeDeriver,\n} from '@sdk-it/core';\n\nconst handlerVisitor: (\n on: (\n node: ts.Node | undefined,\n statusCode: ts.Node | undefined,\n headers: ts.Node | undefined,\n contentType: string,\n ) => void,\n) => ts.Visitor = (callback) => {\n return (node: ts.Node) => {\n if (ts.isReturnStatement(node) && node.expression) {\n if (\n ts.isCallExpression(node.expression) &&\n ts.isPropertyAccessExpression(node.expression.expression)\n ) {\n const propAccess = node.expression.expression;\n if (\n ts.isIdentifier(propAccess.expression) &&\n propAccess.expression.text === 'output'\n ) {\n let contentType = 'application/json';\n const callerMethod = propAccess.name.text;\n const [body, statusCode, headers] = node.expression.arguments;\n if (callerMethod === 'attachment') {\n contentType = 'application/octet-stream';\n }\n if (!body) {\n contentType = 'empty';\n }\n callback(body, statusCode, headers, contentType);\n }\n }\n }\n return ts.forEachChild(node, handlerVisitor(callback));\n };\n};\n\nfunction toResponses(\n handler: ts.ArrowFunction | ts.FunctionExpression,\n deriver: TypeDeriver,\n) {\n const responsesList: ResponseItem[] = [];\n const visit = handlerVisitor((node, statusCode, headers, contentType) => {\n responsesList.push({\n headers: headers ? Object.keys(deriver.serializeNode(headers)) : [],\n contentType,\n statusCode: statusCode ? resolveStatusCode(statusCode) : '200',\n response: node ? deriver.serializeNode(node) : undefined,\n });\n });\n visit(handler.body);\n return responsesList;\n}\n\nfunction resolveStatusCode(node: ts.Node) {\n if (ts.isNumericLiteral(node)) {\n return node.text;\n }\n throw new Error(`Could not resolve status code`);\n}\n\nexport function defaultResponseAnalyzer(\n handler: ts.ArrowFunction | ts.FunctionExpression,\n deriver: TypeDeriver,\n) {\n try {\n return toResponses(handler, deriver);\n } catch (error) {\n console.error('Error analyzing response\\n', handler.getText());\n throw error;\n }\n}\n\nexport const responseAnalyzer: NaunceResponseAnalyzer = {\n 'throw.new.ProblemDetailsException': (handler, deriver, node) => {\n if (ts.isNewExpression(node)) {\n const [problem] = node.arguments ?? [];\n if (!ts.isObjectLiteralExpression(problem)) {\n return [];\n }\n const properties = problem.properties.reduce<Record<string, string>>(\n (acc, prop) => {\n if (ts.isPropertyAssignment(prop)) {\n const key = prop.name.getText();\n if (ts.isLiteralExpression(prop.initializer)) {\n acc[key] = prop.initializer.text;\n } else {\n acc[key] = prop.initializer.getText();\n }\n }\n return acc;\n },\n {},\n );\n return [\n {\n contentType: 'application/problem+json',\n headers: [],\n statusCode: properties.status,\n response: problem ? deriver.serializeNode(problem) : undefined,\n },\n ];\n }\n return [];\n },\n default: defaultResponseAnalyzer,\n};\n\nexport default responseAnalyzer;\n"],
|
|
5
|
+
"mappings": ";AAAA,OAAO,WAAW;AAElB,SAAS,iBAAiB;AAC1B,OAAO,QAAQ;AAEf;AAAA,EAIE;AAAA,EAKA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAKP,SAAS,WAAW,QAAmD;AACrE,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,OAAO,gBAAgB,CAAC;AAC7C,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,aAAa,CAAC,EAAE,cAAc;AACjD,SAAO,YAAY;AACrB;AAKA,SAAS,mBAAmB,QAAwC;AAClE,QAAM,WAAW,WAAW,MAAM;AAClC,SAAO,WAAW,SAAS,SAAS,cAAc,IAAI;AACxD;AAKA,SAAS,gBAAgB,QAAwC;AAC/D,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,SAAO,CAAC,mBAAmB,MAAM;AACnC;AAEO,IAAM,eAAe,CAC1B,MACA,aACA,YACG;AACH,QAAM,SAAmD,CAAC;AAC1D,QAAM,mBAAmB,SAAS,oBAAoB;AACtD,QAAM,WAAW,SAAS,YAAY;AAEtC,QAAM,mBAAmB,oBAAI,IAAoB;AAEjD,QAAM,UAAU,CAACA,OAAe,UAAwB;AAEtD,QAAI,QAAQ,UAAU;AACpB;AAAA,IACF;AAEA,QAAI,GAAG,iBAAiBA,KAAI,GAAG;AAC7B,UAAI,GAAG,gBAAgBA,MAAK,UAAU,GAAG;AACvC,eAAO,KAAK;AAAA,UACV,OAAO,aAAaA,MAAK,WAAW,WAAW,QAAQ,CAAC;AAAA,UACxD,MAAMA,MAAK;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,GAAG,kBAAkBA,KAAI,KAAKA,MAAK,YAAY;AACjD,UAAI,GAAG,iBAAiBA,MAAK,UAAU,GAAG;AACxC,eAAO,KAAK;AAAA,UACV,OAAOA,MAAK,WAAW,WAAW,QAAQ;AAAA,UAC1C,MAAMA,MAAK;AAAA,QACb,CAAC;AAAA,MACH;AAGA,SAAG,aAAaA,MAAK,YAAY,CAAC,UAAU,QAAQ,OAAO,KAAK,CAAC;AACjE;AAAA,IACF;AAGA,QAAI,GAAG,iBAAiBA,KAAI,KAAK,eAAe,QAAQ,UAAU;AAChE,YAAM,iBAAiBA;AAGvB,UAAI;AAEJ,UAAI,GAAG,aAAa,eAAe,UAAU,GAAG;AAC9C,iBAAS,YAAY,oBAAoB,eAAe,UAAU;AAAA,MACpE,WAAW,GAAG,2BAA2B,eAAe,UAAU,GAAG;AACnE,iBAAS,YAAY;AAAA,UACnB,eAAe,WAAW;AAAA,QAC5B;AAAA,MACF;AAGA,UAAI,UAAU,OAAO,QAAQ,GAAG,YAAY,OAAO;AACjD,iBAAS,YAAY,iBAAiB,MAAM;AAAA,MAC9C;AAGA,YAAM,eAAe,oBAAoB,gBAAgB,MAAM;AAE/D,UAAI,gBAAgB,QAAQ;AAC1B,cAAM,eAAe,QAAQ,gBAAgB,CAAC;AAE9C,mBAAW,eAAe,cAAc;AAEtC,cAAI,iBAAiB,IAAI,WAAW,GAAG;AACrC;AAAA,UACF;AAEA,cAAI,mBAAmB,WAAW,KAAK,YAAY,MAAM;AACvD,6BAAiB,IAAI,WAAW;AAEhC,oBAAQ,YAAY,MAAM,QAAQ,CAAC;AAAA,UACrC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,OAAG,aAAaA,OAAM,CAAC,UAAU,QAAQ,OAAO,KAAK,CAAC;AAAA,EACxD;AAEA,UAAQ,MAAM,CAAC;AACf,SAAO;AACT;AAEA,IAAM,SAAS,MAAM,iBAAiB;AAEtC,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,SAAS,kBAAkB,MAAe;AACxC,MAAI,OAAiB,CAAC;AACtB,MAAI,OAAO;AACX,MAAI,cAAc;AAClB,MAAI,UAAU;AACd,MAAI,SAAS;AACb,MAAI,OAAO;AACX,MAAI,kBAAkB;AAEtB,aAAW,OAAO,GAAG;AAAA,IAAgB;AAAA,IAAM,CAACC,SAC1C,WAAW,SAASA,KAAI,QAAQ,IAAkB;AAAA,EACpD,GAAG;AACD,QAAI,OAAO,IAAI,YAAY,UAAU;AACnC;AAAA,IACF;AACA,YAAQ,IAAI,QAAQ,MAAoB;AAAA,MACtC,KAAK;AACH,eAAO,IAAI;AACX;AAAA,MACF,KAAK;AACH,eAAO,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI,CAACA,SAAQA,KAAI,KAAK,CAAC;AACrD;AAAA,MACF,KAAK;AACH,sBAAc,IAAI;AAClB;AAAA,MACF,KAAK;AACH,kBAAU,IAAI;AACd;AAAA,MACF,KAAK;AACH,iBAAS,IAAI,QAAQ,KAAK,EAAE,YAAY;AACxC;AAAA,MACF,KAAK;AACH,eAAO,IAAI,QAAQ,KAAK;AACxB;AAAA,MACF,KAAK;AACH,0BAAkB,IAAI,QAAQ,KAAK;AACnC;AAAA,IACJ;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,MACP,MACAC,mBAKA,OACA,aACA;AACA,MAAI,CAAC,GAAG,iBAAiB,IAAI,KAAK,KAAK,UAAU,SAAS,GAAG;AAC3D,WAAO,OAAO;AAAA,EAChB;AACA,MACE,CAAC,GAAG,2BAA2B,KAAK,UAAU,KAC9C,CAAC,GAAG,aAAa,KAAK,WAAW,IAAI,KACrC,CAAC,aAAa,KAAK,WAAW,KAAK,IAAI,GACvC;AACA,WAAO,OAAO;AAAA,EAChB;AAEA,QAAM,CAAC,QAAQ,IAAI,KAAK;AACxB,MAAI,CAAC,GAAG,gBAAgB,QAAQ,GAAG;AACjC,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,SAAS,KAAK,WAAW,KAAK;AACpC,QAAM,OAAO,SAAS;AACtB,QAAM,WAAW,KAAK,UAAU;AAAA,IAAK,CAAC,QACpC,iBAAiB,KAAK,UAAU;AAAA,EAClC;AACA,MAAI,CAAC,UAAU;AACb,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,UAAU,KAAK,UAAU,GAAG,EAAE;AACpC,MAAI,CAAC,WAAW,CAAC,GAAG,gBAAgB,OAAO,GAAG;AAC5C,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,WAAW,kBAAkB,KAAK,MAAM;AAE9C,MAAI,SAAS,WAAW,WAAW;AACjC,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,gBACJ,SAAS,QACT,UAAU,GAAG,MAAM,IAAI,KAAK,QAAQ,iBAAiB,EAAE,CAAC,EAAE;AAC5D,MAAI,CAAC,SAAS,UAAU,QAAQ;AAC9B,WAAO,OAAO;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACJ,MAAI,SAAS,UAAU,WAAW,GAAG;AACnC,kBAAc,SAAS,UAAU,CAAC;AAClC,eAAW,SAAS,UAAU,CAAC;AAAA,EACjC,OAAO;AACL,eAAW,SAAS,UAAU,CAAC;AAAA,EACjC;AACA,MAAI,CAAC,GAAG,gBAAgB,QAAQ,GAAG;AACjC,WAAO,OAAO;AAAA,EAChB;AACA,MACE,CAAC,YACD,CAAC,GAAG,0BAA0B,SAAS,IAAI,KAC3C,CAAC,GAAG,0BAA0B,SAAS,KAAK,UAAU,GACtD;AACA,WAAO,OAAO;AAAA,EAChB;AAGA,QAAM,yBAAoC,CAAC;AAI3C,aAAW,OAAO,KAAK,UAAU,MAAM,GAAG,EAAE,GAAG;AAC7C,QAAI,GAAG,iBAAiB,GAAG,GAAG;AAE5B,UAAI,GAAG,aAAa,IAAI,UAAU,GAAG;AACnC,YAAI,SAAS,YAAY,oBAAoB,IAAI,UAAU;AAG3D,YAAI,UAAU,OAAO,QAAQ,GAAG,YAAY,OAAO;AACjD,mBAAS,YAAY,iBAAiB,MAAM;AAAA,QAC9C;AAEA,cAAM,kBAAkB;AAAA,UACtB,QAAQ;AAAA,UACR,GAAI,QAAQ,gBAAgB,CAAC;AAAA,QAC/B,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE;AAErB,YAAI,cAAc,gBAAgB,KAAK,kBAAkB;AAGzD,YAAI,CAAC,aAAa;AAChB,qBAAW,QAAQ,iBAAiB;AAClC,gBAAI,GAAG,sBAAsB,IAAI,KAAK,KAAK,aAAa;AACtD,kBAAI,mBAAmB,KAAK,WAAW,GAAG;AACxC,8BAAc,KAAK;AACnB;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,YAAI,aAAa;AACf,iCAAuB,KAAK,WAAW;AAAA,QACzC;AAAA,MACF;AAKA,UACE,GAAG,aAAa,IAAI,UAAU,KAC9B,IAAI,WAAW,SAAS,YACxB;AACA;AAAA,MACF;AACA,YAAM,WAAW,IAAI,UAAU,CAAC;AAChC,UAAI,mBAAmB,QAAQ,GAAG;AAChC,+BAAuB,KAAK,QAAQ;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS,KAAK,WAAW,WAAW;AAAA,IAChD,GAAG;AAAA,EACL;AAEA,QAAM,aAAa,KAAK,cAAc;AAEtC,QAAM,YAA4B,CAAC;AAGnC,aAAW,kBAAkB,wBAAwB;AACnD,eAAW,EAAE,OAAO,MAAAF,MAAK,KAAK,aAAa,gBAAgB,WAAW,GAAG;AACvE,gBAAU,KAAK,GAAGE,kBAAiB,gBAAuB,OAAOF,KAAI,CAAC;AAAA,IACxE;AAAA,EACF;AAGA,aAAW,EAAE,OAAO,MAAAA,MAAK,KAAK,aAAa,SAAS,WAAW,GAAG;AAChE,cAAU,KAAK,GAAGE,kBAAiB,SAAS,OAAOF,KAAI,CAAC;AAAA,EAC1D;AAEA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,cACI,GAAG,gBAAgB,WAAW,IAC5B,YAAY,OACZ,SACF;AAAA,IACJ,YAAY,KAAK;AAAA,IACjB;AAAA,IACA,WAAW;AAAA,IACX;AAAA,EACF;AAEA,WAAS,SAAS;AAChB,OAAG;AAAA,MAAa;AAAA,MAAM,CAACA,UACrB,MAAMA,OAAME,mBAAkB,OAAO,WAAW;AAAA,IAClD;AAAA,EACF;AACF;AAEA,SAAS,YAAY,OAAgC;AACnD,QAAM,YAAwB,CAAC;AAC/B,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,GAAG,0BAA0B,KAAK,WAAW,GAAG;AACnD;AAAA,IACF;AACA,UAAM,OAAO,KAAK,KAAK,QAAQ;AAC/B,UAAM,SAAS,KAAK,YAAY,WAC7B,OAAO,GAAG,oBAAoB,EAC9B,KAAK,CAACC,UAASA,MAAK,KAAK,QAAQ,MAAM,QAAQ;AAClD,QAAI,CAAC,QAAQ;AACX,cAAQ,KAAK,sBAAsB,IAAI,EAAE;AACzC;AAAA,IACF;AACA,UAAM,UAAU,KAAK,YAAY,WAC9B,OAAO,GAAG,oBAAoB,EAC9B,KAAK,CAACA,UAASA,MAAK,KAAK,QAAQ,MAAM,SAAS;AACnD,QAAI,CAAC,SAAS;AACZ,cAAQ,KAAK,uBAAuB,IAAI,EAAE;AAC1C;AAAA,IACF;AACA,UAAM,CAAC,EAAE,QAAQ,UAAU,IAAI,OAAO,YAAY,QAAQ,EAAE,MAAM,GAAG;AACrE,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN,SAAS,QAAQ,YAAY,QAAQ;AAAA,MACrC;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,eAAsB,QACpB,cACA,QASA;AACA,SAAO,kBAAkB;AACzB,QAAM,UAAU,WAAW,YAAY;AACvC,SAAO,iBAAiB;AACxB,QAAM,cAAc,QAAQ,eAAe;AAE3C,SAAO,sBAAsB;AAC7B,QAAM,cAAc,IAAI,YAAY,aAAa,OAAO,QAAQ;AAChE,QAAM,QAAQ,IAAI,MAAM;AAAA,IACtB,SAAS,OAAO,WAAW,CAAC;AAAA,IAC5B,aAAa,OAAO;AAAA,EACtB,CAAC;AAED,aAAW,cAAc,QAAQ,eAAe,GAAG;AACjD,WAAO,aAAa,WAAW,QAAQ,EAAE;AACzC,QAAI,CAAC,WAAW,mBAAmB;AACjC,aAAO,YAAY,WAAW,QAAQ,EAAE;AACxC;AAAA,QACE;AAAA,QACA,CAAC,SAAS,OAAO,SAAS;AACxB,gBAAMD,oBAAmB,OAAO;AAChC,cAAI,OAAOA,sBAAqB,YAAY;AAC1C,kBAAM,SACJA,kBAAiB,KAAK,KAAKA,kBAAiB,SAAS;AACvD,gBAAI,CAAC,QAAQ;AACX,oBAAM,IAAI,MAAM,kCAAkC,KAAK,EAAE;AAAA,YAC3D;AACA,mBAAO,OAAO,SAAS,aAAa,IAAI;AAAA,UAC1C;AACA,iBAAOA,kBAAiB,SAAS,WAAW;AAAA,QAC9C;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAA+B;AAAA,IACnC,SAAS,OAAO,QAAQ,YAAY,SAAS,EAAE;AAAA,MAC7C,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,SAAS,KAAK,EAAE;AAAA,MACzD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,MAAM,MAAM,SAAS;AAAA,IAC5B,MAAM,MAAM,QAAQ;AAAA,IACpB;AAAA,EACF;AACF;AAIA,SAAS,mBACP,MACyE;AACzE,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,UACG,GAAG,sBAAsB,IAAI,KAC5B,GAAG,qBAAqB,IAAI,KAC5B,GAAG,gBAAgB,IAAI,KACvB,GAAG,oBAAoB,IAAI,KAC3B,GAAG,yBAAyB,IAAI,KAChC,GAAG,cAAc,IAAI,KACrB,GAAG,cAAc,IAAI,MACvB,CAAC,CAAC,KAAK;AAEX;;;ACpeA,OAAOE,SAAQ;AAQf,IAAM,iBAOY,CAAC,aAAa;AAC9B,SAAO,CAAC,SAAkB;AACxB,QAAIA,IAAG,kBAAkB,IAAI,KAAK,KAAK,YAAY;AACjD,UACEA,IAAG,iBAAiB,KAAK,UAAU,KACnCA,IAAG,2BAA2B,KAAK,WAAW,UAAU,GACxD;AACA,cAAM,aAAa,KAAK,WAAW;AACnC,YACEA,IAAG,aAAa,WAAW,UAAU,KACrC,WAAW,WAAW,SAAS,UAC/B;AACA,cAAI,cAAc;AAClB,gBAAM,eAAe,WAAW,KAAK;AACrC,gBAAM,CAAC,MAAM,YAAY,OAAO,IAAI,KAAK,WAAW;AACpD,cAAI,iBAAiB,cAAc;AACjC,0BAAc;AAAA,UAChB;AACA,cAAI,CAAC,MAAM;AACT,0BAAc;AAAA,UAChB;AACA,mBAAS,MAAM,YAAY,SAAS,WAAW;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AACA,WAAOA,IAAG,aAAa,MAAM,eAAe,QAAQ,CAAC;AAAA,EACvD;AACF;AAEA,SAAS,YACP,SACA,SACA;AACA,QAAM,gBAAgC,CAAC;AACvC,QAAMC,SAAQ,eAAe,CAAC,MAAM,YAAY,SAAS,gBAAgB;AACvE,kBAAc,KAAK;AAAA,MACjB,SAAS,UAAU,OAAO,KAAK,QAAQ,cAAc,OAAO,CAAC,IAAI,CAAC;AAAA,MAClE;AAAA,MACA,YAAY,aAAa,kBAAkB,UAAU,IAAI;AAAA,MACzD,UAAU,OAAO,QAAQ,cAAc,IAAI,IAAI;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AACD,EAAAA,OAAM,QAAQ,IAAI;AAClB,SAAO;AACT;AAEA,SAAS,kBAAkB,MAAe;AACxC,MAAID,IAAG,iBAAiB,IAAI,GAAG;AAC7B,WAAO,KAAK;AAAA,EACd;AACA,QAAM,IAAI,MAAM,+BAA+B;AACjD;AAEO,SAAS,wBACd,SACA,SACA;AACA,MAAI;AACF,WAAO,YAAY,SAAS,OAAO;AAAA,EACrC,SAAS,OAAO;AACd,YAAQ,MAAM,8BAA8B,QAAQ,QAAQ,CAAC;AAC7D,UAAM;AAAA,EACR;AACF;AAEO,IAAM,mBAA2C;AAAA,EACtD,qCAAqC,CAAC,SAAS,SAAS,SAAS;AAC/D,QAAIA,IAAG,gBAAgB,IAAI,GAAG;AAC5B,YAAM,CAAC,OAAO,IAAI,KAAK,aAAa,CAAC;AACrC,UAAI,CAACA,IAAG,0BAA0B,OAAO,GAAG;AAC1C,eAAO,CAAC;AAAA,MACV;AACA,YAAM,aAAa,QAAQ,WAAW;AAAA,QACpC,CAAC,KAAK,SAAS;AACb,cAAIA,IAAG,qBAAqB,IAAI,GAAG;AACjC,kBAAM,MAAM,KAAK,KAAK,QAAQ;AAC9B,gBAAIA,IAAG,oBAAoB,KAAK,WAAW,GAAG;AAC5C,kBAAI,GAAG,IAAI,KAAK,YAAY;AAAA,YAC9B,OAAO;AACL,kBAAI,GAAG,IAAI,KAAK,YAAY,QAAQ;AAAA,YACtC;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AAAA,QACA,CAAC;AAAA,MACH;AACA,aAAO;AAAA,QACL;AAAA,UACE,aAAa;AAAA,UACb,SAAS,CAAC;AAAA,UACV,YAAY,WAAW;AAAA,UACvB,UAAU,UAAU,QAAQ,cAAc,OAAO,IAAI;AAAA,QACvD;AAAA,MACF;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AAAA,EACA,SAAS;AACX;",
|
|
6
6
|
"names": ["node", "tag", "responseAnalyzer", "prop", "ts", "visit"]
|
|
7
7
|
}
|
package/dist/lib/generic.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ComponentsObject } from 'openapi3-ts/oas31';
|
|
2
2
|
import ts from 'typescript';
|
|
3
3
|
import { type InjectImport, type NaunceResponseAnalyzer, type OnOperation, type ResponseAnalyzerFn } from '@sdk-it/core';
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const returnTokens: (node: ts.Node, typeChecker?: ts.TypeChecker, options?: {
|
|
5
|
+
consider3rdParty?: boolean;
|
|
6
|
+
maxDepth?: number;
|
|
7
|
+
}) => {
|
|
5
8
|
token: string;
|
|
6
9
|
node: ts.Expression;
|
|
7
10
|
}[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic.d.ts","sourceRoot":"","sources":["../../src/lib/generic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAEhB,KAAK,kBAAkB,EASxB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"generic.d.ts","sourceRoot":"","sources":["../../src/lib/generic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAEhB,KAAK,kBAAkB,EASxB,MAAM,cAAc,CAAC;AAsCtB,eAAO,MAAM,YAAY,GACvB,MAAM,EAAE,CAAC,IAAI,EACb,cAAc,EAAE,CAAC,WAAW,EAC5B,UAAU;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;WAEpC,MAAM;UAAQ,EAAE,CAAC,UAAU;GAgFnD,CAAC;AAsQF,wBAAsB,OAAO,CAC3B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE;IACN;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,gBAAgB,EAAE,kBAAkB,GAAG,sBAAsB,CAAC;IAC9D,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;;;;GAkDF;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import type { NaunceResponseAnalyzer, ResponseItem, TypeDeriver } from '@sdk-it/core';
|
|
3
|
-
export declare function defaultResponseAnalyzer(handler: ts.ArrowFunction, deriver: TypeDeriver): ResponseItem[];
|
|
3
|
+
export declare function defaultResponseAnalyzer(handler: ts.ArrowFunction | ts.FunctionExpression, deriver: TypeDeriver): ResponseItem[];
|
|
4
4
|
export declare const responseAnalyzer: NaunceResponseAnalyzer;
|
|
5
|
+
export default responseAnalyzer;
|
|
5
6
|
//# sourceMappingURL=response-analyzer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-analyzer.d.ts","sourceRoot":"","sources":["../../src/lib/response-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,WAAW,EACZ,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"response-analyzer.d.ts","sourceRoot":"","sources":["../../src/lib/response-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,WAAW,EACZ,MAAM,cAAc,CAAC;AA8DtB,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,EACjD,OAAO,EAAE,WAAW,kBAQrB;AAED,eAAO,MAAM,gBAAgB,EAAE,sBAiC9B,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdk-it/generic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"!**/*.test.*"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@sdk-it/core": "0.
|
|
25
|
+
"@sdk-it/core": "0.39.0",
|
|
26
26
|
"stringcase": "^4.3.1",
|
|
27
27
|
"debug": "^4.4.0",
|
|
28
28
|
"openapi3-ts": "^4.4.0"
|