@saptools/service-flow 0.1.36 → 0.1.37
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.
|
@@ -998,47 +998,51 @@ function isSupportedEventReceiver(receiver, rootReceiver, serviceVariables) {
|
|
|
998
998
|
}
|
|
999
999
|
function collectWrapperSpecs(source) {
|
|
1000
1000
|
const specs = /* @__PURE__ */ new Map();
|
|
1001
|
+
const serviceVariables = collectServiceVariables(source);
|
|
1002
|
+
const calledNames = /* @__PURE__ */ new Set();
|
|
1003
|
+
const collectCalls = (node) => {
|
|
1004
|
+
if (ts4.isCallExpression(node)) {
|
|
1005
|
+
if (ts4.isIdentifier(node.expression)) calledNames.add(node.expression.text);
|
|
1006
|
+
if (ts4.isCallExpression(node.expression) && ts4.isIdentifier(node.expression.expression)) calledNames.add(node.expression.expression.text);
|
|
1007
|
+
}
|
|
1008
|
+
ts4.forEachChild(node, collectCalls);
|
|
1009
|
+
};
|
|
1010
|
+
collectCalls(source);
|
|
1001
1011
|
const scanFunction = (name, fn) => {
|
|
1012
|
+
if (!calledNames.has(name)) return;
|
|
1002
1013
|
const params = fn.parameters.map((param) => ts4.isIdentifier(param.name) ? param.name.text : void 0);
|
|
1003
|
-
const closure = returnedClosure(fn);
|
|
1004
|
-
if (!closure) return;
|
|
1005
1014
|
const sends = [];
|
|
1006
1015
|
const visit = (node) => {
|
|
1007
1016
|
if (ts4.isCallExpression(node) && ts4.isPropertyAccessExpression(node.expression) && node.expression.name.text === "send" && ts4.isIdentifier(node.expression.expression)) {
|
|
1008
1017
|
const objectArg = node.arguments[0];
|
|
1009
1018
|
if (objectArg && ts4.isObjectLiteralExpression(objectArg)) {
|
|
1010
|
-
const pathProp = objectArg
|
|
1011
|
-
|
|
1019
|
+
const pathProp = propertyInitializer(objectArg, "path");
|
|
1020
|
+
const methodProp = propertyInitializer(objectArg, "method");
|
|
1021
|
+
const pathName = pathProp && ts4.isIdentifier(pathProp) ? pathProp.text : void 0;
|
|
1022
|
+
const methodName = methodProp && ts4.isIdentifier(methodProp) ? methodProp.text : void 0;
|
|
1023
|
+
const methodLiteral = staticExpressionText(methodProp, variableInitializers(source));
|
|
1024
|
+
if (pathName) sends.push({ client: node.expression.expression.text, path: pathName, method: methodName, methodLiteral, start: node.getStart(source), end: node.getEnd() });
|
|
1012
1025
|
}
|
|
1013
1026
|
}
|
|
1014
1027
|
ts4.forEachChild(node, visit);
|
|
1015
1028
|
};
|
|
1016
|
-
visit(
|
|
1029
|
+
visit(fn);
|
|
1017
1030
|
if (sends.length !== 1) return;
|
|
1018
1031
|
const found = sends[0];
|
|
1019
1032
|
const clientIndex = params.indexOf(found.client);
|
|
1020
1033
|
const pathIndex = params.indexOf(found.path);
|
|
1021
|
-
const methodIndex = found.method
|
|
1022
|
-
|
|
1034
|
+
const methodIndex = found.method ? params.indexOf(found.method) : -1;
|
|
1035
|
+
const capturesKnownClient = serviceVariables.has(found.client) || /^(srv|service|serviceClient|client|.*Client)$/.test(found.client);
|
|
1036
|
+
if (pathIndex >= 0 && (clientIndex >= 0 || capturesKnownClient)) specs.set(name, { clientIndex: clientIndex >= 0 ? clientIndex : void 0, clientName: clientIndex >= 0 ? void 0 : found.client, pathIndex, methodIndex: methodIndex >= 0 ? methodIndex : void 0, methodName: found.method, methodLiteral: found.methodLiteral, definitionLine: lineOf3(source.text, fn.getStart(source)), internalStart: found.start, internalEnd: found.end });
|
|
1023
1037
|
};
|
|
1024
|
-
|
|
1025
|
-
if (ts4.isFunctionDeclaration(
|
|
1026
|
-
if (ts4.
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1038
|
+
const visitTop = (node) => {
|
|
1039
|
+
if (ts4.isFunctionDeclaration(node) && node.name) scanFunction(node.name.text, node);
|
|
1040
|
+
if (ts4.isVariableDeclaration(node) && ts4.isIdentifier(node.name) && node.initializer && (ts4.isArrowFunction(node.initializer) || ts4.isFunctionExpression(node.initializer))) scanFunction(node.name.text, node.initializer);
|
|
1041
|
+
ts4.forEachChild(node, visitTop);
|
|
1042
|
+
};
|
|
1043
|
+
visitTop(source);
|
|
1030
1044
|
return specs;
|
|
1031
1045
|
}
|
|
1032
|
-
function returnedClosure(fn) {
|
|
1033
|
-
const body = fn.body;
|
|
1034
|
-
if (!body) return void 0;
|
|
1035
|
-
if (ts4.isArrowFunction(fn) && !ts4.isBlock(body)) return ts4.isArrowFunction(body) || ts4.isFunctionExpression(body) ? body : void 0;
|
|
1036
|
-
if (!ts4.isBlock(body)) return void 0;
|
|
1037
|
-
const returns = body.statements.filter(ts4.isReturnStatement);
|
|
1038
|
-
if (returns.length !== 1) return void 0;
|
|
1039
|
-
const expr = returns[0]?.expression;
|
|
1040
|
-
return expr && (ts4.isArrowFunction(expr) || ts4.isFunctionExpression(expr)) ? expr : void 0;
|
|
1041
|
-
}
|
|
1042
1046
|
function classifyOutboundCallsInSource(source, filePath) {
|
|
1043
1047
|
const calls = [];
|
|
1044
1048
|
const sourceFile = normalizePath(filePath);
|
|
@@ -1067,26 +1071,31 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
1067
1071
|
const receiver = receiverName(expr.expression);
|
|
1068
1072
|
const query = objectPropertyText(objectArg, "query");
|
|
1069
1073
|
const method = stripQuotes(objectPropertyText(objectArg, "method") ?? "POST");
|
|
1070
|
-
const
|
|
1074
|
+
const pathExpr = propertyInitializer(objectArg, "path") ?? propertyInitializer(objectArg, "event");
|
|
1075
|
+
const op = pathExpr ? staticExpressionText(pathExpr, initializers) ?? pathExpr.getText(source) : void 0;
|
|
1071
1076
|
const shorthandPath = objectPropertyIsShorthand(objectArg, "path");
|
|
1072
|
-
const
|
|
1077
|
+
const staticPath = staticExpressionText(pathExpr, initializers);
|
|
1078
|
+
const literalPath = isStringLike(pathExpr) ? pathExpr.text : pathExpr && ts4.isTemplateExpression(pathExpr) ? pathExpr.getText(source) : void 0;
|
|
1079
|
+
const operationPathExpr = staticPath ? `/${stripQuotes(staticPath).replace(/^\//, "")}` : literalPath ? `/${stripQuotes(literalPath).replace(/^\//, "")}` : void 0;
|
|
1073
1080
|
const intent = classifyODataPathIntent(operationPathExpr, method);
|
|
1074
1081
|
const entityCallTypes = { entity_mutation: "remote_entity_mutation", entity_delete: "remote_entity_delete", entity_media: "remote_entity_media", entity_candidate: "remote_entity_candidate" };
|
|
1075
1082
|
const entityCallType = entityCallTypes[intent.kind];
|
|
1076
1083
|
const isODataQueryRead = method.toUpperCase() === "GET" && ["entity_query", "entity_key_read", "entity_navigation_query"].includes(intent.kind);
|
|
1077
|
-
add(node, { callType: query ? "remote_query" : entityCallType ?? (isODataQueryRead ? "remote_query" : "remote_action"), serviceVariableName: receiver, method, operationPathExpr, queryEntity: query ? extractQueryEntity(query) : isODataQueryRead ? intent.entitySegment : void 0, payloadSummary: summarizeExpression(objectArg.getText(source)), confidence: op || query ? 0.8 : 0.4, unresolvedReason: !query &&
|
|
1084
|
+
add(node, { callType: query ? "remote_query" : entityCallType ?? (isODataQueryRead ? "remote_query" : "remote_action"), serviceVariableName: receiver, method, operationPathExpr, queryEntity: query ? extractQueryEntity(query) : isODataQueryRead ? intent.entitySegment : void 0, payloadSummary: summarizeExpression(objectArg.getText(source)), confidence: op || query ? 0.8 : 0.4, unresolvedReason: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 }, { receiver, classifier: "service_client_send_object", operationPathExpression: shorthandPath ? op : void 0, literalPathSource: shorthandPath && staticPath ? "same_scope_const_initializer" : void 0, odataPathIntent: operationPathExpr ? intent : void 0, parserWarning: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 });
|
|
1078
1085
|
}
|
|
1079
1086
|
} else if (ts4.isCallExpression(expr) && ts4.isIdentifier(expr.expression) && wrapperSpecs.has(expr.expression.text) || ts4.isIdentifier(expr) && wrapperSpecs.has(expr.text)) {
|
|
1080
1087
|
const wrapperName = ts4.isIdentifier(expr) ? expr.text : ts4.isCallExpression(expr) && ts4.isIdentifier(expr.expression) ? expr.expression.text : "";
|
|
1081
1088
|
const wrapperArgs = ts4.isIdentifier(expr) ? node.arguments : ts4.isCallExpression(expr) ? expr.arguments : node.arguments;
|
|
1082
1089
|
const spec = wrapperSpecs.get(wrapperName);
|
|
1083
|
-
const clientArg = spec ? wrapperArgs[spec.clientIndex]
|
|
1090
|
+
const clientArg = spec?.clientIndex === void 0 ? void 0 : wrapperArgs[spec.clientIndex];
|
|
1084
1091
|
const pathArg = spec ? wrapperArgs[spec.pathIndex] : void 0;
|
|
1085
1092
|
const methodArg = spec?.methodIndex === void 0 ? void 0 : wrapperArgs[spec.methodIndex];
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
add(node, { callType: "remote_action", serviceVariableName:
|
|
1093
|
+
const receiver = clientArg && ts4.isIdentifier(clientArg) ? clientArg.text : spec?.clientName;
|
|
1094
|
+
const method = stripQuotes(staticExpressionText(methodArg, initializers) ?? spec?.methodLiteral ?? "POST");
|
|
1095
|
+
if (spec && receiver && isStringLike(pathArg)) {
|
|
1096
|
+
add(node, { callType: "remote_action", serviceVariableName: receiver, method, operationPathExpr: `/${pathArg.text.replace(/^\//, "")}`, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.75 }, { receiver, classifier: "higher_order_wrapper_literal_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), literalPathSource: "wrapper_call_argument", literalCallerArgumentDetected: true });
|
|
1097
|
+
} else if (spec && receiver) {
|
|
1098
|
+
add(node, { callType: "remote_action", serviceVariableName: receiver, method, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.45, unresolvedReason: "dynamic_operation_path_identifier" }, { receiver, classifier: "higher_order_wrapper_dynamic_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), parserWarning: "dynamic_operation_path_identifier" });
|
|
1090
1099
|
}
|
|
1091
1100
|
} else if (ts4.isPropertyAccessExpression(expr) && ["emit", "publish", "on"].includes(expr.name.text)) {
|
|
1092
1101
|
const receiver = receiverName(expr.expression);
|
|
@@ -1257,6 +1266,17 @@ function unwrapIdentityExpression(expr) {
|
|
|
1257
1266
|
if (ts5.isTypeAssertionExpression(expr)) return unwrapIdentityExpression(expr.expression);
|
|
1258
1267
|
return expr;
|
|
1259
1268
|
}
|
|
1269
|
+
function transactionReceiverName(expr) {
|
|
1270
|
+
const call = unwrapCall(expr);
|
|
1271
|
+
if (call && ts5.isPropertyAccessExpression(call.expression) && ["tx", "transaction"].includes(call.expression.name.text) && ts5.isIdentifier(call.expression.expression)) return call.expression.expression.text;
|
|
1272
|
+
const unwrapped = unwrapIdentityExpression(expr);
|
|
1273
|
+
if (ts5.isConditionalExpression(unwrapped)) {
|
|
1274
|
+
const left = transactionReceiverName(unwrapped.whenTrue);
|
|
1275
|
+
const right = transactionReceiverName(unwrapped.whenFalse);
|
|
1276
|
+
return left && left === right ? left : void 0;
|
|
1277
|
+
}
|
|
1278
|
+
return void 0;
|
|
1279
|
+
}
|
|
1260
1280
|
function findConnectInExpression(expr) {
|
|
1261
1281
|
const direct = unwrapCall(expr);
|
|
1262
1282
|
if (direct) {
|
|
@@ -1343,9 +1363,8 @@ function collectLocalBindingFacts(fn) {
|
|
|
1343
1363
|
if (ts5.isVariableDeclaration(node) && ts5.isIdentifier(node.name) && node.initializer) {
|
|
1344
1364
|
const fact = findConnectInExpression(node.initializer);
|
|
1345
1365
|
if (fact) bindings.set(node.name.text, fact);
|
|
1346
|
-
const
|
|
1347
|
-
if (
|
|
1348
|
-
const sourceName = call.expression.expression.text;
|
|
1366
|
+
const sourceName = transactionReceiverName(node.initializer);
|
|
1367
|
+
if (sourceName) {
|
|
1349
1368
|
const source = bindings.get(sourceName);
|
|
1350
1369
|
if (source) bindings.set(node.name.text, { ...source, helperChain: [...source.helperChain ?? [], { aliasOf: sourceName, callerVariable: node.name.text, aliasKind: "transaction", transactionAliasSource: sourceName }] });
|
|
1351
1370
|
}
|
|
@@ -1548,7 +1567,8 @@ async function parseServiceBindings(repoPath, filePath) {
|
|
|
1548
1567
|
callerVariable: targetName,
|
|
1549
1568
|
aliasOf: sourceName,
|
|
1550
1569
|
aliasKind,
|
|
1551
|
-
scopeRule: "same-file-source-order"
|
|
1570
|
+
scopeRule: "same-file-source-order",
|
|
1571
|
+
...aliasKind === "transaction" ? { transactionAliasSource: sourceName } : {}
|
|
1552
1572
|
}
|
|
1553
1573
|
]
|
|
1554
1574
|
});
|
|
@@ -1780,8 +1800,9 @@ async function parseServiceBindings(repoPath, filePath) {
|
|
|
1780
1800
|
recordDestructuredClassHelper(decl);
|
|
1781
1801
|
await recordVariable(decl);
|
|
1782
1802
|
recordIdentityAlias(decl);
|
|
1783
|
-
if (ts5.isIdentifier(decl.name) && decl.initializer
|
|
1784
|
-
|
|
1803
|
+
if (ts5.isIdentifier(decl.name) && decl.initializer) {
|
|
1804
|
+
const sourceName = transactionReceiverName(decl.initializer);
|
|
1805
|
+
if (sourceName) cloneAliasBinding(decl.name.text, sourceName, "transaction", decl);
|
|
1785
1806
|
}
|
|
1786
1807
|
continue;
|
|
1787
1808
|
}
|
|
@@ -3058,4 +3079,4 @@ export {
|
|
|
3058
3079
|
linkWorkspace,
|
|
3059
3080
|
trace
|
|
3060
3081
|
};
|
|
3061
|
-
//# sourceMappingURL=chunk-
|
|
3082
|
+
//# sourceMappingURL=chunk-WE3A6TOJ.js.map
|