@kernlang/review 3.4.6-canary.44.1.a85ee2e8 → 3.4.6-canary.46.1.19dcfc19
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/dist/mappers/ts-concepts/extractors/dependency.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/dependency.js +28 -0
- package/dist/mappers/ts-concepts/extractors/dependency.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/effect.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/effect.js +93 -0
- package/dist/mappers/ts-concepts/extractors/effect.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/index.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/index.js +117 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/index.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/mount.d.ts +2 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/mount.js +105 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/mount.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/pagination.d.ts +7 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/pagination.js +128 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/pagination.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/route-handler.d.ts +25 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/route-handler.js +110 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/route-handler.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/success-status.d.ts +5 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/success-status.js +150 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/success-status.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/validation.d.ts +14 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/validation.js +172 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-express/validation.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-nextjs.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-nextjs.js +100 -0
- package/dist/mappers/ts-concepts/extractors/entrypoint-nextjs.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/error-handle.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/error-handle.js +113 -0
- package/dist/mappers/ts-concepts/extractors/error-handle.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/error-raise.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/error-raise.js +47 -0
- package/dist/mappers/ts-concepts/extractors/error-raise.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/function-decls.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/function-decls.js +145 -0
- package/dist/mappers/ts-concepts/extractors/function-decls.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/guard.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/guard.js +94 -0
- package/dist/mappers/ts-concepts/extractors/guard.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/react-wrappers.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/react-wrappers.js +55 -0
- package/dist/mappers/ts-concepts/extractors/react-wrappers.js.map +1 -0
- package/dist/mappers/ts-concepts/extractors/state-mutation.d.ts +3 -0
- package/dist/mappers/ts-concepts/extractors/state-mutation.js +100 -0
- package/dist/mappers/ts-concepts/extractors/state-mutation.js.map +1 -0
- package/dist/mappers/ts-concepts/helpers/ast.d.ts +14 -0
- package/dist/mappers/ts-concepts/helpers/ast.js +162 -0
- package/dist/mappers/ts-concepts/helpers/ast.js.map +1 -0
- package/dist/mappers/ts-concepts/helpers/const-resolution.d.ts +7 -0
- package/dist/mappers/ts-concepts/helpers/const-resolution.js +186 -0
- package/dist/mappers/ts-concepts/helpers/const-resolution.js.map +1 -0
- package/dist/mappers/ts-concepts/helpers/error-paths.d.ts +16 -0
- package/dist/mappers/ts-concepts/helpers/error-paths.js +308 -0
- package/dist/mappers/ts-concepts/helpers/error-paths.js.map +1 -0
- package/dist/mappers/ts-concepts/helpers/network-call.d.ts +46 -0
- package/dist/mappers/ts-concepts/helpers/network-call.js +398 -0
- package/dist/mappers/ts-concepts/helpers/network-call.js.map +1 -0
- package/dist/mappers/ts-concepts/helpers/types.d.ts +30 -0
- package/dist/mappers/ts-concepts/helpers/types.js +426 -0
- package/dist/mappers/ts-concepts/helpers/types.js.map +1 -0
- package/dist/mappers/ts-concepts/helpers/wrapped-client.d.ts +23 -0
- package/dist/mappers/ts-concepts/helpers/wrapped-client.js +95 -0
- package/dist/mappers/ts-concepts/helpers/wrapped-client.js.map +1 -0
- package/dist/mappers/ts-concepts/index.d.ts +9 -0
- package/dist/mappers/ts-concepts/index.js +39 -0
- package/dist/mappers/ts-concepts/index.js.map +1 -0
- package/dist/mappers/ts-concepts/signatures.d.ts +40 -0
- package/dist/mappers/ts-concepts/signatures.js +233 -0
- package/dist/mappers/ts-concepts/signatures.js.map +1 -0
- package/dist/mappers/ts-concepts.d.ts +1 -9
- package/dist/mappers/ts-concepts.js +1 -3552
- package/dist/mappers/ts-concepts.js.map +1 -1
- package/dist/python-fallback/extractors/dependency.d.ts +4 -0
- package/dist/python-fallback/extractors/dependency.js +22 -0
- package/dist/python-fallback/extractors/dependency.js.map +1 -0
- package/dist/python-fallback/extractors/error.d.ts +4 -0
- package/dist/python-fallback/extractors/error.js +44 -0
- package/dist/python-fallback/extractors/error.js.map +1 -0
- package/dist/python-fallback/extractors/fastapi-pagination.d.ts +10 -0
- package/dist/python-fallback/extractors/fastapi-pagination.js +161 -0
- package/dist/python-fallback/extractors/fastapi-pagination.js.map +1 -0
- package/dist/python-fallback/extractors/fastapi-status.d.ts +12 -0
- package/dist/python-fallback/extractors/fastapi-status.js +104 -0
- package/dist/python-fallback/extractors/fastapi-status.js.map +1 -0
- package/dist/python-fallback/extractors/pydantic.d.ts +13 -0
- package/dist/python-fallback/extractors/pydantic.js +59 -0
- package/dist/python-fallback/extractors/pydantic.js.map +1 -0
- package/dist/python-fallback/extractors/routes.d.ts +6 -0
- package/dist/python-fallback/extractors/routes.js +34 -0
- package/dist/python-fallback/extractors/routes.js.map +1 -0
- package/dist/python-fallback/helpers/lines.d.ts +22 -0
- package/dist/python-fallback/helpers/lines.js +60 -0
- package/dist/python-fallback/helpers/lines.js.map +1 -0
- package/dist/python-fallback/helpers/types.d.ts +7 -0
- package/dist/python-fallback/helpers/types.js +135 -0
- package/dist/python-fallback/helpers/types.js.map +1 -0
- package/dist/python-fallback/index.d.ts +2 -0
- package/dist/python-fallback/index.js +270 -0
- package/dist/python-fallback/index.js.map +1 -0
- package/dist/python-fallback/signatures.d.ts +15 -0
- package/dist/python-fallback/signatures.js +76 -0
- package/dist/python-fallback/signatures.js.map +1 -0
- package/dist/python-fallback.d.ts +1 -2
- package/dist/python-fallback.js +1 -928
- package/dist/python-fallback.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { conceptId } from '@kernlang/core';
|
|
2
|
+
import { span } from '../helpers/ast.js';
|
|
3
|
+
import { NODE_STDLIB } from '../signatures.js';
|
|
4
|
+
export function extractDependencyEdges(sf, filePath, edges) {
|
|
5
|
+
for (const imp of sf.getImportDeclarations()) {
|
|
6
|
+
const specifier = imp.getModuleSpecifierValue();
|
|
7
|
+
const start = imp.getStart();
|
|
8
|
+
let subtype = 'external';
|
|
9
|
+
if (specifier.startsWith('.') || specifier.startsWith('/')) {
|
|
10
|
+
subtype = 'internal';
|
|
11
|
+
}
|
|
12
|
+
else if (NODE_STDLIB.has(specifier.split('/')[0]) || specifier.startsWith('node:')) {
|
|
13
|
+
subtype = 'stdlib';
|
|
14
|
+
}
|
|
15
|
+
edges.push({
|
|
16
|
+
id: conceptId(filePath, 'dependency', start),
|
|
17
|
+
kind: 'dependency',
|
|
18
|
+
sourceId: filePath,
|
|
19
|
+
targetId: specifier,
|
|
20
|
+
primarySpan: span(filePath, imp),
|
|
21
|
+
evidence: imp.getText().substring(0, 120),
|
|
22
|
+
confidence: 1.0,
|
|
23
|
+
language: 'ts',
|
|
24
|
+
payload: { kind: 'dependency', subtype, specifier },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=dependency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency.js","sourceRoot":"","sources":["../../../../src/mappers/ts-concepts/extractors/dependency.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,UAAU,sBAAsB,CAAC,EAAc,EAAE,QAAgB,EAAE,KAAoB;IAC3F,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,qBAAqB,EAAE,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,GAAG,CAAC,uBAAuB,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QAE7B,IAAI,OAAO,GAAuC,UAAU,CAAC;QAC7D,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3D,OAAO,GAAG,UAAU,CAAC;QACvB,CAAC;aAAM,IAAI,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACrF,OAAO,GAAG,QAAQ,CAAC;QACrB,CAAC;QAED,KAAK,CAAC,IAAI,CAAC;YACT,EAAE,EAAE,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC;YAC5C,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,SAAS;YACnB,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YAChC,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;YACzC,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE;SACpD,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { conceptId } from '@kernlang/core';
|
|
2
|
+
import { SyntaxKind } from 'ts-morph';
|
|
3
|
+
import { getContainerId, isInAsyncContext, span } from '../helpers/ast.js';
|
|
4
|
+
import { buildConstLiteralMap } from '../helpers/const-resolution.js';
|
|
5
|
+
import { extractHandledErrorStatusCodes, extractHandlesApiErrors, isResponseAsserted } from '../helpers/error-paths.js';
|
|
6
|
+
import { extractAuthPropagation, extractBodyKind, extractHasAuthHeader, extractHost, extractHttpMethod, extractQueryParams, extractSentFields, extractTarget, } from '../helpers/network-call.js';
|
|
7
|
+
import { collectClientIdentifiers } from '../helpers/wrapped-client.js';
|
|
8
|
+
import { CLIENT_HTTP_METHODS, DB_CALLS, FS_CALLS, NETWORK_CALLS, NETWORK_METHODS } from '../signatures.js';
|
|
9
|
+
export function extractEffects(sf, filePath, nodes) {
|
|
10
|
+
const clientIdents = collectClientIdentifiers(sf);
|
|
11
|
+
const constLiterals = buildConstLiteralMap(sf);
|
|
12
|
+
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
13
|
+
const callee = call.getExpression();
|
|
14
|
+
let funcName = '';
|
|
15
|
+
let objName = '';
|
|
16
|
+
if (callee.getKind() === SyntaxKind.Identifier) {
|
|
17
|
+
funcName = callee.getText();
|
|
18
|
+
}
|
|
19
|
+
else if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
20
|
+
const pa = callee;
|
|
21
|
+
funcName = pa.getName();
|
|
22
|
+
objName = pa.getExpression().getText();
|
|
23
|
+
}
|
|
24
|
+
// Network effects
|
|
25
|
+
const isDirectNetwork = NETWORK_CALLS.has(funcName);
|
|
26
|
+
const isKnownLibraryMethod = NETWORK_METHODS.has(funcName) && /axios|got|ky|http|request|superagent/i.test(objName);
|
|
27
|
+
const isWrappedClientCall = CLIENT_HTTP_METHODS.has(funcName) && clientIdents.has(objName);
|
|
28
|
+
if (isDirectNetwork || isKnownLibraryMethod || isWrappedClientCall) {
|
|
29
|
+
const isAsync = isInAsyncContext(call);
|
|
30
|
+
const target = extractTarget(call, constLiterals);
|
|
31
|
+
const sentFieldsInfo = extractSentFields(call, funcName);
|
|
32
|
+
const queryParamsInfo = extractQueryParams(call, constLiterals);
|
|
33
|
+
const hasAuthHeader = extractHasAuthHeader(call, funcName);
|
|
34
|
+
nodes.push({
|
|
35
|
+
id: conceptId(filePath, 'effect', call.getStart()),
|
|
36
|
+
kind: 'effect',
|
|
37
|
+
primarySpan: span(filePath, call),
|
|
38
|
+
evidence: call.getText().substring(0, 120),
|
|
39
|
+
confidence: isDirectNetwork ? 1.0 : isWrappedClientCall ? 0.75 : 0.8,
|
|
40
|
+
language: 'ts',
|
|
41
|
+
containerId: getContainerId(call, filePath),
|
|
42
|
+
payload: {
|
|
43
|
+
kind: 'effect',
|
|
44
|
+
subtype: 'network',
|
|
45
|
+
async: isAsync,
|
|
46
|
+
target,
|
|
47
|
+
host: extractHost(target),
|
|
48
|
+
handledErrorStatusCodes: extractHandledErrorStatusCodes(call),
|
|
49
|
+
responseAsserted: isResponseAsserted(call, isWrappedClientCall),
|
|
50
|
+
bodyKind: extractBodyKind(call, funcName),
|
|
51
|
+
method: extractHttpMethod(call, funcName, isDirectNetwork, isKnownLibraryMethod, isWrappedClientCall),
|
|
52
|
+
hasAuthHeader,
|
|
53
|
+
sentFields: sentFieldsInfo.fields,
|
|
54
|
+
sentFieldsResolved: sentFieldsInfo.resolved,
|
|
55
|
+
sentFieldTypes: sentFieldsInfo.types,
|
|
56
|
+
handlesApiErrors: extractHandlesApiErrors(call, isWrappedClientCall),
|
|
57
|
+
authPropagation: extractAuthPropagation(call, funcName, objName, isWrappedClientCall, hasAuthHeader),
|
|
58
|
+
queryParams: queryParamsInfo.params,
|
|
59
|
+
queryParamsResolved: queryParamsInfo.resolved,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// DB effects
|
|
65
|
+
if (DB_CALLS.has(funcName) && /db|prisma|mongo|pool|client|knex|sequelize|typeorm|drizzle/i.test(objName)) {
|
|
66
|
+
nodes.push({
|
|
67
|
+
id: conceptId(filePath, 'effect', call.getStart()),
|
|
68
|
+
kind: 'effect',
|
|
69
|
+
primarySpan: span(filePath, call),
|
|
70
|
+
evidence: call.getText().substring(0, 120),
|
|
71
|
+
confidence: 0.85,
|
|
72
|
+
language: 'ts',
|
|
73
|
+
containerId: getContainerId(call, filePath),
|
|
74
|
+
payload: { kind: 'effect', subtype: 'db', async: isInAsyncContext(call) },
|
|
75
|
+
});
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
// FS effects
|
|
79
|
+
if (FS_CALLS.has(funcName)) {
|
|
80
|
+
nodes.push({
|
|
81
|
+
id: conceptId(filePath, 'effect', call.getStart()),
|
|
82
|
+
kind: 'effect',
|
|
83
|
+
primarySpan: span(filePath, call),
|
|
84
|
+
evidence: call.getText().substring(0, 120),
|
|
85
|
+
confidence: 0.95,
|
|
86
|
+
language: 'ts',
|
|
87
|
+
containerId: getContainerId(call, filePath),
|
|
88
|
+
payload: { kind: 'effect', subtype: 'fs', async: funcName.includes('Sync') ? false : isInAsyncContext(call) },
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=effect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect.js","sourceRoot":"","sources":["../../../../src/mappers/ts-concepts/extractors/effect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { conceptId } from '@kernlang/core';
|
|
2
|
+
import { SyntaxKind } from 'ts-morph';
|
|
3
|
+
import { getContainerId, isSameConceptSourceFile, span } from '../../helpers/ast.js';
|
|
4
|
+
import { ROUTE_METHODS } from '../../signatures.js';
|
|
5
|
+
import { extractRouteMount } from './mount.js';
|
|
6
|
+
import { analyzeExpressRouteHandler, EMPTY_ROUTE_ANALYSIS, resolveExpressRouteHandler } from './route-handler.js';
|
|
7
|
+
import { extractHandlerBodyFields } from './validation.js';
|
|
8
|
+
export function extractEntrypoints(sf, filePath, nodes) {
|
|
9
|
+
// Express/Fastify route handlers: app.get('/path', handler)
|
|
10
|
+
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
11
|
+
const callee = call.getExpression();
|
|
12
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
13
|
+
continue;
|
|
14
|
+
const pa = callee;
|
|
15
|
+
const methodName = pa.getName();
|
|
16
|
+
const objText = pa.getExpression().getText();
|
|
17
|
+
if (!/app|router|server/i.test(objText))
|
|
18
|
+
continue;
|
|
19
|
+
const args = call.getArguments();
|
|
20
|
+
if (args.length < 2)
|
|
21
|
+
continue;
|
|
22
|
+
if (methodName === 'use') {
|
|
23
|
+
const mount = extractRouteMount(call, filePath);
|
|
24
|
+
if (mount)
|
|
25
|
+
nodes.push(mount);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (!ROUTE_METHODS.has(methodName))
|
|
29
|
+
continue;
|
|
30
|
+
// First arg is the route path
|
|
31
|
+
let routePath;
|
|
32
|
+
if (args[0].getKind() === SyntaxKind.StringLiteral) {
|
|
33
|
+
routePath = args[0].getLiteralValue();
|
|
34
|
+
}
|
|
35
|
+
// Inline, same-file named, or statically imported handler. The id we
|
|
36
|
+
// compute for same-file handlers must match the function_declaration
|
|
37
|
+
// concept emitted for the same function, so downstream rules can
|
|
38
|
+
// dereference `handlerConceptId`.
|
|
39
|
+
const resolvedHandler = resolveExpressRouteHandler(args, filePath);
|
|
40
|
+
const handlerFn = resolvedHandler?.fn;
|
|
41
|
+
const handlerConceptId = resolvedHandler && isSameConceptSourceFile(resolvedHandler.conceptFilePath, filePath)
|
|
42
|
+
? conceptId(filePath, 'function_declaration', resolvedHandler.conceptStart)
|
|
43
|
+
: undefined;
|
|
44
|
+
const bodyFieldsInfo = handlerFn
|
|
45
|
+
? extractHandlerBodyFields(handlerFn)
|
|
46
|
+
: {
|
|
47
|
+
fields: undefined,
|
|
48
|
+
resolved: false,
|
|
49
|
+
types: undefined,
|
|
50
|
+
};
|
|
51
|
+
const routeAnalysis = handlerFn
|
|
52
|
+
? analyzeExpressRouteHandler(handlerFn, args, methodName.toUpperCase(), routePath)
|
|
53
|
+
: EMPTY_ROUTE_ANALYSIS;
|
|
54
|
+
nodes.push({
|
|
55
|
+
id: conceptId(filePath, 'entrypoint', call.getStart()),
|
|
56
|
+
kind: 'entrypoint',
|
|
57
|
+
primarySpan: span(filePath, call),
|
|
58
|
+
evidence: call.getText().substring(0, 120),
|
|
59
|
+
confidence: 0.95,
|
|
60
|
+
language: 'ts',
|
|
61
|
+
containerId: getContainerId(call, filePath),
|
|
62
|
+
payload: {
|
|
63
|
+
kind: 'entrypoint',
|
|
64
|
+
subtype: 'route',
|
|
65
|
+
name: routePath || methodName,
|
|
66
|
+
httpMethod: methodName.toUpperCase(),
|
|
67
|
+
handlerConceptId,
|
|
68
|
+
bodyFields: bodyFieldsInfo.fields,
|
|
69
|
+
bodyFieldsResolved: bodyFieldsInfo.resolved,
|
|
70
|
+
bodyFieldTypes: bodyFieldsInfo.types,
|
|
71
|
+
errorStatusCodes: routeAnalysis.errorStatusCodes,
|
|
72
|
+
successStatusCodes: routeAnalysis.successStatusCodes,
|
|
73
|
+
successStatusCodesResolved: routeAnalysis.successStatusCodesResolved,
|
|
74
|
+
paginationStrategy: routeAnalysis.paginationStrategy,
|
|
75
|
+
paginationStrategyResolved: routeAnalysis.paginationStrategyResolved,
|
|
76
|
+
hasUnboundedCollectionQuery: routeAnalysis.hasUnboundedCollectionQuery,
|
|
77
|
+
hasDbWrite: routeAnalysis.hasDbWrite,
|
|
78
|
+
hasIdempotencyProtection: routeAnalysis.hasIdempotencyProtection,
|
|
79
|
+
hasBodyValidation: routeAnalysis.hasBodyValidation,
|
|
80
|
+
validatedBodyFields: routeAnalysis.validatedBodyFields,
|
|
81
|
+
bodyValidationResolved: routeAnalysis.bodyValidationResolved,
|
|
82
|
+
validatedBodyFieldTypes: routeAnalysis.validatedBodyFieldTypes,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// export default function — only if it looks like a handler (has req/res params or returns JSX)
|
|
87
|
+
for (const exportDecl of sf.getExportedDeclarations()) {
|
|
88
|
+
const [name, decls] = exportDecl;
|
|
89
|
+
if (name !== 'default')
|
|
90
|
+
continue;
|
|
91
|
+
for (const decl of decls) {
|
|
92
|
+
if (decl.getKind() === SyntaxKind.FunctionDeclaration) {
|
|
93
|
+
const fn = decl;
|
|
94
|
+
const params = fn.getParameters();
|
|
95
|
+
const paramNames = params.map((p) => p.getName());
|
|
96
|
+
const isHandler = paramNames.some((n) => /req|request|ctx|context|event/i.test(n));
|
|
97
|
+
const isComponent = fn.getName()?.[0]?.toUpperCase() === fn.getName()?.[0];
|
|
98
|
+
if (isHandler || isComponent) {
|
|
99
|
+
nodes.push({
|
|
100
|
+
id: conceptId(filePath, 'entrypoint', decl.getStart()),
|
|
101
|
+
kind: 'entrypoint',
|
|
102
|
+
primarySpan: span(filePath, decl),
|
|
103
|
+
evidence: decl.getText().substring(0, 100),
|
|
104
|
+
confidence: isHandler ? 0.9 : 0.7,
|
|
105
|
+
language: 'ts',
|
|
106
|
+
payload: {
|
|
107
|
+
kind: 'entrypoint',
|
|
108
|
+
subtype: isHandler ? 'handler' : 'export',
|
|
109
|
+
name: fn.getName() || 'default',
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/mappers/ts-concepts/extractors/entrypoint-express/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { conceptId } from '@kernlang/core';
|
|
2
|
+
import { SyntaxKind } from 'ts-morph';
|
|
3
|
+
import { getContainerId, span } from '../../helpers/ast.js';
|
|
4
|
+
// Express `app.use('/api/prefix', ...middlewares, subRouter)`: emit a
|
|
5
|
+
// route-mount concept so `collectRoutesAcrossGraph` can join the sub-router's
|
|
6
|
+
// bare paths (`router.get('/foo')`) with the mount prefix (`/api/prefix/foo`).
|
|
7
|
+
// Mirrors the FastAPI `app.include_router()` emission in review-python.
|
|
8
|
+
// Caller must have already filtered to `.use()` calls whose object matches
|
|
9
|
+
// `app|router|server`.
|
|
10
|
+
export function extractRouteMount(call, mountFile) {
|
|
11
|
+
const args = call.getArguments();
|
|
12
|
+
if (args.length < 2)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (args[0].getKind() !== SyntaxKind.StringLiteral)
|
|
15
|
+
return undefined;
|
|
16
|
+
const prefix = args[0].getLiteralValue();
|
|
17
|
+
if (!prefix.startsWith('/'))
|
|
18
|
+
return undefined;
|
|
19
|
+
// The sub-router is the LAST arg; intermediate args are middlewares.
|
|
20
|
+
const last = args[args.length - 1];
|
|
21
|
+
if (last.getKind() !== SyntaxKind.Identifier)
|
|
22
|
+
return undefined;
|
|
23
|
+
const ident = last;
|
|
24
|
+
const routerName = ident.getText();
|
|
25
|
+
const sourceModule = resolveImportedFileSuffix(ident, mountFile);
|
|
26
|
+
return {
|
|
27
|
+
id: conceptId(mountFile, 'entrypoint', call.getStart()),
|
|
28
|
+
kind: 'entrypoint',
|
|
29
|
+
primarySpan: span(mountFile, call),
|
|
30
|
+
evidence: call.getText().substring(0, 120),
|
|
31
|
+
confidence: 0.9,
|
|
32
|
+
language: 'ts',
|
|
33
|
+
containerId: getContainerId(call, mountFile),
|
|
34
|
+
payload: {
|
|
35
|
+
kind: 'entrypoint',
|
|
36
|
+
subtype: 'route-mount',
|
|
37
|
+
name: prefix,
|
|
38
|
+
routerName,
|
|
39
|
+
sourceModule,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Given an identifier used in a mount call, resolve it back to the file it
|
|
44
|
+
// was imported from, and return a trailing path suffix (relative to the mount
|
|
45
|
+
// file's directory, with extension) usable by `cross-stack-utils`'s
|
|
46
|
+
// `routeFile.endsWith('/' + sourceModule)` matcher.
|
|
47
|
+
function resolveImportedFileSuffix(ident, mountFile) {
|
|
48
|
+
const symbol = ident.getSymbol();
|
|
49
|
+
if (!symbol)
|
|
50
|
+
return undefined;
|
|
51
|
+
for (const decl of symbol.getDeclarations()) {
|
|
52
|
+
const kind = decl.getKind();
|
|
53
|
+
if (kind !== SyntaxKind.ImportClause && kind !== SyntaxKind.ImportSpecifier)
|
|
54
|
+
continue;
|
|
55
|
+
const importDecl = decl.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
|
|
56
|
+
if (!importDecl)
|
|
57
|
+
continue;
|
|
58
|
+
const resolved = importDecl.getModuleSpecifierSourceFile();
|
|
59
|
+
if (resolved) {
|
|
60
|
+
return pathSuffixBetween(mountFile, resolved.getFilePath());
|
|
61
|
+
}
|
|
62
|
+
const specifier = importDecl.getModuleSpecifierValue();
|
|
63
|
+
const guess = guessResolvedSuffix(specifier, mountFile);
|
|
64
|
+
if (guess)
|
|
65
|
+
return guess;
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
function pathSuffixBetween(mountFile, targetFile) {
|
|
70
|
+
const parts = targetFile.split('/');
|
|
71
|
+
const mountParts = mountFile.split('/');
|
|
72
|
+
let commonLen = 0;
|
|
73
|
+
while (commonLen < parts.length - 1 &&
|
|
74
|
+
commonLen < mountParts.length - 1 &&
|
|
75
|
+
parts[commonLen] === mountParts[commonLen]) {
|
|
76
|
+
commonLen++;
|
|
77
|
+
}
|
|
78
|
+
const tail = parts.slice(commonLen).join('/');
|
|
79
|
+
return tail || parts.slice(-2).join('/');
|
|
80
|
+
}
|
|
81
|
+
function guessResolvedSuffix(specifier, mountFile) {
|
|
82
|
+
if (!specifier.startsWith('.'))
|
|
83
|
+
return undefined;
|
|
84
|
+
const mountDir = mountFile.split('/').slice(0, -1).join('/');
|
|
85
|
+
const segments = [];
|
|
86
|
+
for (const seg of specifier.split('/')) {
|
|
87
|
+
if (seg === '' || seg === '.')
|
|
88
|
+
continue;
|
|
89
|
+
if (seg === '..') {
|
|
90
|
+
if (mountDir.length === 0)
|
|
91
|
+
continue;
|
|
92
|
+
segments.pop();
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
segments.push(seg);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (segments.length === 0)
|
|
99
|
+
return undefined;
|
|
100
|
+
const base = segments[segments.length - 1];
|
|
101
|
+
const swapped = base.replace(/\.(js|mjs|cjs)$/i, '.ts');
|
|
102
|
+
segments[segments.length - 1] = /\.(ts|tsx)$/i.test(swapped) ? swapped : `${swapped}.ts`;
|
|
103
|
+
return segments.join('/');
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=mount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../../../../../src/mappers/ts-concepts/extractors/entrypoint-express/mount.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5D,sEAAsE;AACtE,8EAA8E;AAC9E,+EAA+E;AAC/E,wEAAwE;AACxE,2EAA2E;AAC3E,uBAAuB;AACvB,MAAM,UAAU,iBAAiB,CAAC,IAAuC,EAAE,SAAiB;IAC1F,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACtC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,aAAa;QAAE,OAAO,SAAS,CAAC;IACrE,MAAM,MAAM,GAAI,IAAI,CAAC,CAAC,CAAsC,CAAC,eAAe,EAAE,CAAC;IAC/E,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAE9C,qEAAqE;IACrE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAC/D,MAAM,KAAK,GAAG,IAAqC,CAAC;IACpD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IAEnC,MAAM,YAAY,GAAG,yBAAyB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACjE,OAAO;QACL,EAAE,EAAE,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACvD,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC;QAClC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;QAC1C,UAAU,EAAE,GAAG;QACf,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC;QAC5C,OAAO,EAAE;YACP,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,aAAa;YACtB,IAAI,EAAE,MAAM;YACZ,UAAU;YACV,YAAY;SACb;KACF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,oEAAoE;AACpE,oDAAoD;AACpD,SAAS,yBAAyB,CAAC,KAAoC,EAAE,SAAiB;IACxF,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;IACjC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,eAAe,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,IAAI,IAAI,KAAK,UAAU,CAAC,YAAY,IAAI,IAAI,KAAK,UAAU,CAAC,eAAe;YAAE,SAAS;QACtF,MAAM,UAAU,GAAG,IAAI,CAAC,sBAAsB,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QAC7E,IAAI,CAAC,UAAU;YAAE,SAAS;QAC1B,MAAM,QAAQ,GAAG,UAAU,CAAC,4BAA4B,EAAE,CAAC;QAC3D,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,SAAS,GAAG,UAAU,CAAC,uBAAuB,EAAE,CAAC;QACvD,MAAM,KAAK,GAAG,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACxD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB,EAAE,UAAkB;IAC9D,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,OACE,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC;QAC5B,SAAS,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC;QACjC,KAAK,CAAC,SAAS,CAAC,KAAK,UAAU,CAAC,SAAS,CAAC,EAC1C,CAAC;QACD,SAAS,EAAE,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9C,OAAO,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,mBAAmB,CAAC,SAAiB,EAAE,SAAiB;IAC/D,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACjD,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG;YAAE,SAAS;QACxC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACpC,QAAQ,CAAC,GAAG,EAAE,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACxD,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC;IACzF,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExpressRouteHandlerFn } from './route-handler.js';
|
|
2
|
+
export declare function extractExpressPaginationStrategy(handlerFn: ExpressRouteHandlerFn): {
|
|
3
|
+
strategy: 'page' | 'offset' | 'cursor' | 'mixed' | 'none' | undefined;
|
|
4
|
+
resolved: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function hasUnboundedExpressCollectionQuery(handlerFn: ExpressRouteHandlerFn, method: string, routePath: string | undefined): boolean;
|
|
7
|
+
export declare function handlerHasDbWrite(handlerFn: ExpressRouteHandlerFn): boolean;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { classifyPaginationAnchor } from '../../../../concept-rules/cross-stack-utils.js';
|
|
3
|
+
import { DB_COLLECTION_READ_CALLS, DB_WRITE_CALLS, PAGINATION_RE } from '../../signatures.js';
|
|
4
|
+
// ── Express pagination-strategy extraction ───────────────────────────────
|
|
5
|
+
// Walks for `req.query.X` / `req.query['X']` / destructuring `const {a, b} =
|
|
6
|
+
// req.query`, classifies each key against anchor sets, and returns the strategy.
|
|
7
|
+
// Size keys (`limit`, `take`, `pageSize`, `perPage`) are pagination *parameters*
|
|
8
|
+
// but not anchor families on their own — a server reading only `limit` is
|
|
9
|
+
// classified as `'none'` (it doesn't pin a strategy).
|
|
10
|
+
//
|
|
11
|
+
// Anchor sets are imported from `concept-rules/cross-stack-utils` so the
|
|
12
|
+
// server-side classification stays in lockstep with the client-side one used
|
|
13
|
+
// by `pagination-key-drift`.
|
|
14
|
+
function classifyAnchor(key) {
|
|
15
|
+
return classifyPaginationAnchor(key);
|
|
16
|
+
}
|
|
17
|
+
export function extractExpressPaginationStrategy(handlerFn) {
|
|
18
|
+
const families = new Set();
|
|
19
|
+
let sawDynamicAccess = false;
|
|
20
|
+
// Pattern 1: `req.query.X` / `req.query[<lit>]` / destructuring
|
|
21
|
+
for (const access of handlerFn.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)) {
|
|
22
|
+
if (access.getName() !== 'query')
|
|
23
|
+
continue;
|
|
24
|
+
const reqIdent = access.getExpression();
|
|
25
|
+
if (!/\b(req|request)\b/i.test(reqIdent.getText()))
|
|
26
|
+
continue;
|
|
27
|
+
const parent = access.getParent();
|
|
28
|
+
// Form 1: `req.query.X` — direct dotted read
|
|
29
|
+
if (parent && Node.isPropertyAccessExpression(parent) && parent.getExpression() === access) {
|
|
30
|
+
const family = classifyAnchor(parent.getName());
|
|
31
|
+
if (family)
|
|
32
|
+
families.add(family);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
// Form 2: `req.query['X']` — element access with literal
|
|
36
|
+
if (parent && Node.isElementAccessExpression(parent) && parent.getExpression() === access) {
|
|
37
|
+
const arg = parent.getArgumentExpression();
|
|
38
|
+
if (arg && Node.isStringLiteral(arg)) {
|
|
39
|
+
const family = classifyAnchor(arg.getLiteralValue());
|
|
40
|
+
if (family)
|
|
41
|
+
families.add(family);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
sawDynamicAccess = true;
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
// Form 3: `const { page, limit } = req.query` — destructuring
|
|
49
|
+
if (parent && Node.isVariableDeclaration(parent) && parent.getInitializer() === access) {
|
|
50
|
+
const nameNode = parent.getNameNode();
|
|
51
|
+
if (Node.isObjectBindingPattern(nameNode)) {
|
|
52
|
+
for (const elem of nameNode.getElements()) {
|
|
53
|
+
if (elem.getDotDotDotToken()) {
|
|
54
|
+
sawDynamicAccess = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const keyNode = elem.getPropertyNameNode() ?? elem.getNameNode();
|
|
58
|
+
if (Node.isIdentifier(keyNode)) {
|
|
59
|
+
const family = classifyAnchor(keyNode.getText());
|
|
60
|
+
if (family)
|
|
61
|
+
families.add(family);
|
|
62
|
+
}
|
|
63
|
+
else if (Node.isStringLiteral(keyNode)) {
|
|
64
|
+
const family = classifyAnchor(keyNode.getLiteralValue());
|
|
65
|
+
if (family)
|
|
66
|
+
families.add(family);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
sawDynamicAccess = true;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
sawDynamicAccess = true;
|
|
75
|
+
}
|
|
76
|
+
if (sawDynamicAccess) {
|
|
77
|
+
return { strategy: undefined, resolved: false };
|
|
78
|
+
}
|
|
79
|
+
if (families.size === 0) {
|
|
80
|
+
return { strategy: 'none', resolved: true };
|
|
81
|
+
}
|
|
82
|
+
if (families.size > 1)
|
|
83
|
+
return { strategy: 'mixed', resolved: true };
|
|
84
|
+
const only = families.values().next().value;
|
|
85
|
+
return { strategy: only, resolved: true };
|
|
86
|
+
}
|
|
87
|
+
export function hasUnboundedExpressCollectionQuery(handlerFn, method, routePath) {
|
|
88
|
+
if (method !== 'GET')
|
|
89
|
+
return false;
|
|
90
|
+
if (routePath && /[:{]/.test(routePath))
|
|
91
|
+
return false;
|
|
92
|
+
const text = handlerFn.getText();
|
|
93
|
+
if (PAGINATION_RE.test(text) || /\b(req|request)\.query\b/.test(text))
|
|
94
|
+
return false;
|
|
95
|
+
if (!handlerHasDbCollectionRead(handlerFn))
|
|
96
|
+
return false;
|
|
97
|
+
if (!/\.json\s*\(|send\s*\(/.test(text))
|
|
98
|
+
return false;
|
|
99
|
+
const lastSegment = routePath?.split('/').filter(Boolean).pop();
|
|
100
|
+
return Boolean(lastSegment?.endsWith('s')) || /\bfindMany\b|\.find\s*\(|\.toArray\s*\(/.test(text);
|
|
101
|
+
}
|
|
102
|
+
function handlerHasDbCollectionRead(handlerFn) {
|
|
103
|
+
return handlerFn.getDescendantsOfKind(SyntaxKind.CallExpression).some((call) => {
|
|
104
|
+
const callee = call.getExpression();
|
|
105
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
106
|
+
return false;
|
|
107
|
+
const pa = callee;
|
|
108
|
+
if (!DB_COLLECTION_READ_CALLS.has(pa.getName()))
|
|
109
|
+
return false;
|
|
110
|
+
return isDbLikeReceiver(pa.getExpression().getText());
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
export function handlerHasDbWrite(handlerFn) {
|
|
114
|
+
return handlerFn.getDescendantsOfKind(SyntaxKind.CallExpression).some((call) => {
|
|
115
|
+
const callee = call.getExpression();
|
|
116
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
117
|
+
return false;
|
|
118
|
+
const pa = callee;
|
|
119
|
+
if (!DB_WRITE_CALLS.has(pa.getName()))
|
|
120
|
+
return false;
|
|
121
|
+
return isDbLikeReceiver(pa.getExpression().getText());
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function isDbLikeReceiver(receiver) {
|
|
125
|
+
return (/\b(db|prisma|mongo|collection|repo|repository|model|client|knex|sequelize|typeorm|pool)\b/i.test(receiver) ||
|
|
126
|
+
/^[A-Z][A-Za-z0-9_]*(Model)?$/.test(receiver));
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../../../src/mappers/ts-concepts/extractors/entrypoint-express/pagination.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FieldTypeMap } from '../../helpers/types.js';
|
|
2
|
+
export type ExpressRouteHandlerFn = import('ts-morph').ArrowFunction | import('ts-morph').FunctionExpression | import('ts-morph').FunctionDeclaration;
|
|
3
|
+
export interface ExpressRouteHandlerResolution {
|
|
4
|
+
fn: ExpressRouteHandlerFn;
|
|
5
|
+
conceptStart: number;
|
|
6
|
+
conceptFilePath: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RouteHandlerAnalysis {
|
|
9
|
+
errorStatusCodes?: readonly number[];
|
|
10
|
+
successStatusCodes?: readonly number[];
|
|
11
|
+
successStatusCodesResolved?: boolean;
|
|
12
|
+
paginationStrategy?: 'page' | 'offset' | 'cursor' | 'mixed' | 'none';
|
|
13
|
+
paginationStrategyResolved?: boolean;
|
|
14
|
+
hasUnboundedCollectionQuery?: boolean;
|
|
15
|
+
hasDbWrite?: boolean;
|
|
16
|
+
hasIdempotencyProtection?: boolean;
|
|
17
|
+
hasBodyValidation?: boolean;
|
|
18
|
+
validatedBodyFields?: readonly string[];
|
|
19
|
+
bodyValidationResolved?: boolean;
|
|
20
|
+
validatedBodyFieldTypes?: FieldTypeMap;
|
|
21
|
+
}
|
|
22
|
+
export declare const EMPTY_ROUTE_ANALYSIS: RouteHandlerAnalysis;
|
|
23
|
+
export declare function analyzeExpressRouteHandler(handlerFn: ExpressRouteHandlerFn, routeArgs: readonly import('ts-morph').Node[], method: string, routePath: string | undefined): RouteHandlerAnalysis;
|
|
24
|
+
export declare function resolveExpressRouteHandler(routeArgs: readonly import('ts-morph').Node[], filePath: string): ExpressRouteHandlerResolution | undefined;
|
|
25
|
+
export declare function extractExpressErrorStatusCodes(handlerFn: ExpressRouteHandlerFn): readonly number[] | undefined;
|