@saptools/service-flow 0.1.54 → 0.1.56
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/CHANGELOG.md +10 -0
- package/README.md +13 -5
- package/TECHNICAL-NOTE.md +12 -0
- package/dist/{chunk-ERIZHM5C.js → chunk-Y7H7ZU5B.js} +352 -113
- package/dist/chunk-Y7H7ZU5B.js.map +1 -0
- package/dist/cli.js +60 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli.ts +24 -16
- package/src/output/000-stdout-policy.ts +65 -0
- package/src/parsers/000-direct-query-execution.ts +287 -0
- package/src/parsers/outbound-call-parser.ts +53 -11
- package/dist/chunk-ERIZHM5C.js.map +0 -1
|
@@ -2303,7 +2303,7 @@ function collectClassHelpers(sf) {
|
|
|
2303
2303
|
// src/parsers/outbound-call-parser.ts
|
|
2304
2304
|
import fs8 from "fs/promises";
|
|
2305
2305
|
import path11 from "path";
|
|
2306
|
-
import
|
|
2306
|
+
import ts9 from "typescript";
|
|
2307
2307
|
|
|
2308
2308
|
// src/linker/external-http-target.ts
|
|
2309
2309
|
import { createHash } from "crypto";
|
|
@@ -3019,53 +3019,287 @@ function literal(expr) {
|
|
|
3019
3019
|
return expr && (ts7.isStringLiteralLike(expr) || ts7.isNoSubstitutionTemplateLiteral(expr)) ? expr.text : void 0;
|
|
3020
3020
|
}
|
|
3021
3021
|
|
|
3022
|
+
// src/parsers/000-direct-query-execution.ts
|
|
3023
|
+
import ts8 from "typescript";
|
|
3024
|
+
var capQueryBuilderRoots = /* @__PURE__ */ new Set([
|
|
3025
|
+
"SELECT.from",
|
|
3026
|
+
"SELECT.one.from",
|
|
3027
|
+
"SELECT.one",
|
|
3028
|
+
"INSERT.into",
|
|
3029
|
+
"UPSERT.into",
|
|
3030
|
+
"UPDATE.entity",
|
|
3031
|
+
"UPDATE",
|
|
3032
|
+
"DELETE.from"
|
|
3033
|
+
]);
|
|
3034
|
+
var promiseValueShadowCache = /* @__PURE__ */ new WeakMap();
|
|
3035
|
+
function isCapQueryBuilderRootName(name) {
|
|
3036
|
+
return capQueryBuilderRoots.has(name);
|
|
3037
|
+
}
|
|
3038
|
+
function queryBuilderRoot(expression) {
|
|
3039
|
+
const unwrapped = unwrapQueryExpression(expression);
|
|
3040
|
+
if (!ts8.isCallExpression(unwrapped)) return void 0;
|
|
3041
|
+
if (isCapQueryBuilderRootName(expressionName(unwrapped.expression)))
|
|
3042
|
+
return unwrapped;
|
|
3043
|
+
return ts8.isPropertyAccessExpression(unwrapped.expression) ? queryBuilderRoot(unwrapped.expression.expression) : void 0;
|
|
3044
|
+
}
|
|
3045
|
+
function directQueryBuilderStatement(node) {
|
|
3046
|
+
const root = queryBuilderRoot(node);
|
|
3047
|
+
if (!root) return void 0;
|
|
3048
|
+
const logicalCall = outerFluentQueryCall(root);
|
|
3049
|
+
if (logicalCall !== node) return void 0;
|
|
3050
|
+
const expression = outerTransparentExpression(logicalCall);
|
|
3051
|
+
const awaitExpression = directAwaitExpression(expression);
|
|
3052
|
+
if (awaitExpression)
|
|
3053
|
+
return { root, logicalCall, statement: awaitExpression, executionContext: "await" };
|
|
3054
|
+
const returnContext = returnExecutionContext(expression);
|
|
3055
|
+
if (returnContext)
|
|
3056
|
+
return { root, logicalCall, statement: expression, executionContext: returnContext };
|
|
3057
|
+
if (isAwaitedPromiseAllElement(expression))
|
|
3058
|
+
return { root, logicalCall, statement: expression, executionContext: "promise_aggregate" };
|
|
3059
|
+
return void 0;
|
|
3060
|
+
}
|
|
3061
|
+
function expressionName(expression) {
|
|
3062
|
+
if (ts8.isIdentifier(expression)) return expression.text;
|
|
3063
|
+
if (ts8.isPropertyAccessExpression(expression))
|
|
3064
|
+
return `${expressionName(expression.expression)}.${expression.name.text}`;
|
|
3065
|
+
return expression.getText();
|
|
3066
|
+
}
|
|
3067
|
+
function unwrapQueryExpression(expression) {
|
|
3068
|
+
if (ts8.isParenthesizedExpression(expression) || ts8.isAwaitExpression(expression) || ts8.isAsExpression(expression) || ts8.isTypeAssertionExpression(expression) || ts8.isNonNullExpression(expression) || ts8.isSatisfiesExpression(expression))
|
|
3069
|
+
return unwrapQueryExpression(expression.expression);
|
|
3070
|
+
return expression;
|
|
3071
|
+
}
|
|
3072
|
+
function wrapperParent(node) {
|
|
3073
|
+
const parent = node.parent;
|
|
3074
|
+
if ((ts8.isParenthesizedExpression(parent) || ts8.isAsExpression(parent) || ts8.isTypeAssertionExpression(parent) || ts8.isNonNullExpression(parent) || ts8.isSatisfiesExpression(parent)) && parent.expression === node)
|
|
3075
|
+
return parent;
|
|
3076
|
+
return void 0;
|
|
3077
|
+
}
|
|
3078
|
+
function fluentCallParent(node) {
|
|
3079
|
+
const property = node.parent;
|
|
3080
|
+
if (!ts8.isPropertyAccessExpression(property) || property.expression !== node)
|
|
3081
|
+
return void 0;
|
|
3082
|
+
const call = property.parent;
|
|
3083
|
+
return ts8.isCallExpression(call) && call.expression === property ? call : void 0;
|
|
3084
|
+
}
|
|
3085
|
+
function outerFluentQueryCall(root) {
|
|
3086
|
+
let current = root;
|
|
3087
|
+
let outer = root;
|
|
3088
|
+
while (true) {
|
|
3089
|
+
const wrapper = wrapperParent(current);
|
|
3090
|
+
if (wrapper) {
|
|
3091
|
+
current = wrapper;
|
|
3092
|
+
continue;
|
|
3093
|
+
}
|
|
3094
|
+
const next = fluentCallParent(current);
|
|
3095
|
+
if (!next) return outer;
|
|
3096
|
+
outer = next;
|
|
3097
|
+
current = next;
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
function outerTransparentExpression(expression) {
|
|
3101
|
+
let current = expression;
|
|
3102
|
+
while (true) {
|
|
3103
|
+
const wrapper = wrapperParent(current);
|
|
3104
|
+
if (!wrapper) return current;
|
|
3105
|
+
current = wrapper;
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
function directAwaitExpression(expression) {
|
|
3109
|
+
const parent = expression.parent;
|
|
3110
|
+
return ts8.isAwaitExpression(parent) && parent.expression === expression ? parent : void 0;
|
|
3111
|
+
}
|
|
3112
|
+
function returnExecutionContext(expression) {
|
|
3113
|
+
const callable = returnedExpressionCallable(expression);
|
|
3114
|
+
if (!callable) return void 0;
|
|
3115
|
+
if (hasAsyncModifier(callable)) return "async_return";
|
|
3116
|
+
return hasGuaranteedPromiseReturn(callable) ? "promise_return" : void 0;
|
|
3117
|
+
}
|
|
3118
|
+
function returnedExpressionCallable(expression) {
|
|
3119
|
+
const parent = expression.parent;
|
|
3120
|
+
if (ts8.isArrowFunction(parent) && parent.body === expression) return parent;
|
|
3121
|
+
if (!ts8.isReturnStatement(parent) || parent.expression !== expression)
|
|
3122
|
+
return void 0;
|
|
3123
|
+
return nearestCallable(parent);
|
|
3124
|
+
}
|
|
3125
|
+
function nearestCallable(node) {
|
|
3126
|
+
let current = node.parent;
|
|
3127
|
+
while (current) {
|
|
3128
|
+
if (isRuntimeCallable(current)) return current;
|
|
3129
|
+
current = current.parent;
|
|
3130
|
+
}
|
|
3131
|
+
return void 0;
|
|
3132
|
+
}
|
|
3133
|
+
function isRuntimeCallable(node) {
|
|
3134
|
+
return ts8.isFunctionDeclaration(node) || ts8.isFunctionExpression(node) || ts8.isArrowFunction(node) || ts8.isMethodDeclaration(node) || ts8.isConstructorDeclaration(node) || ts8.isGetAccessorDeclaration(node) || ts8.isSetAccessorDeclaration(node);
|
|
3135
|
+
}
|
|
3136
|
+
function hasAsyncModifier(node) {
|
|
3137
|
+
return !isGeneratorCallable(node) && ts8.canHaveModifiers(node) && (ts8.getModifiers(node)?.some(
|
|
3138
|
+
(modifier) => modifier.kind === ts8.SyntaxKind.AsyncKeyword
|
|
3139
|
+
) ?? false);
|
|
3140
|
+
}
|
|
3141
|
+
function isGeneratorCallable(node) {
|
|
3142
|
+
return (ts8.isFunctionDeclaration(node) || ts8.isFunctionExpression(node) || ts8.isMethodDeclaration(node)) && Boolean(node.asteriskToken);
|
|
3143
|
+
}
|
|
3144
|
+
function hasGuaranteedPromiseReturn(callable) {
|
|
3145
|
+
const returnType = declaredReturnType(callable);
|
|
3146
|
+
return Boolean(returnType && isGuaranteedPromiseType(returnType));
|
|
3147
|
+
}
|
|
3148
|
+
function declaredReturnType(callable) {
|
|
3149
|
+
if (ts8.isFunctionDeclaration(callable) || ts8.isFunctionExpression(callable) || ts8.isArrowFunction(callable) || ts8.isMethodDeclaration(callable))
|
|
3150
|
+
return callable.type;
|
|
3151
|
+
return void 0;
|
|
3152
|
+
}
|
|
3153
|
+
function isGuaranteedPromiseType(type) {
|
|
3154
|
+
if (ts8.isParenthesizedTypeNode(type))
|
|
3155
|
+
return isGuaranteedPromiseType(type.type);
|
|
3156
|
+
if (ts8.isTypeReferenceNode(type))
|
|
3157
|
+
return isStandardPromiseTypeName(type.typeName);
|
|
3158
|
+
if (ts8.isUnionTypeNode(type))
|
|
3159
|
+
return type.types.length > 0 && type.types.every(isGuaranteedPromiseType);
|
|
3160
|
+
if (ts8.isIntersectionTypeNode(type))
|
|
3161
|
+
return type.types.some(isGuaranteedPromiseType);
|
|
3162
|
+
return false;
|
|
3163
|
+
}
|
|
3164
|
+
function isStandardPromiseTypeName(name) {
|
|
3165
|
+
if (ts8.isIdentifier(name))
|
|
3166
|
+
return name.text === "Promise" || name.text === "PromiseLike";
|
|
3167
|
+
return ts8.isIdentifier(name.left) && (name.left.text === "globalThis" || name.left.text === "global") && (name.right.text === "Promise" || name.right.text === "PromiseLike");
|
|
3168
|
+
}
|
|
3169
|
+
function isAwaitedPromiseAllElement(expression) {
|
|
3170
|
+
const array = directArrayParent(expression);
|
|
3171
|
+
if (!array) return false;
|
|
3172
|
+
const aggregate = aggregateCallForArray(array);
|
|
3173
|
+
return Boolean(aggregate && isBuiltInPromiseAll(aggregate) && directAwaitExpression(outerTransparentExpression(aggregate)));
|
|
3174
|
+
}
|
|
3175
|
+
function directArrayParent(expression) {
|
|
3176
|
+
const parent = expression.parent;
|
|
3177
|
+
return ts8.isArrayLiteralExpression(parent) && parent.elements.some(
|
|
3178
|
+
(element) => element === expression
|
|
3179
|
+
) ? parent : void 0;
|
|
3180
|
+
}
|
|
3181
|
+
function aggregateCallForArray(array) {
|
|
3182
|
+
const argument = outerTransparentExpression(array);
|
|
3183
|
+
const parent = argument.parent;
|
|
3184
|
+
return ts8.isCallExpression(parent) && parent.arguments.length === 1 && parent.arguments[0] === argument ? parent : void 0;
|
|
3185
|
+
}
|
|
3186
|
+
function isBuiltInPromiseAll(call) {
|
|
3187
|
+
return ts8.isPropertyAccessExpression(call.expression) && ts8.isIdentifier(call.expression.expression) && call.expression.expression.text === "Promise" && call.expression.name.text === "all" && !hasPromiseValueShadow(call.getSourceFile());
|
|
3188
|
+
}
|
|
3189
|
+
function hasPromiseValueShadow(source) {
|
|
3190
|
+
const cached = promiseValueShadowCache.get(source);
|
|
3191
|
+
if (cached !== void 0) return cached;
|
|
3192
|
+
let shadowed = false;
|
|
3193
|
+
const visit = (node) => {
|
|
3194
|
+
if (shadowed) return;
|
|
3195
|
+
if (declaresPromiseValue(node)) {
|
|
3196
|
+
shadowed = true;
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
3199
|
+
ts8.forEachChild(node, visit);
|
|
3200
|
+
};
|
|
3201
|
+
visit(source);
|
|
3202
|
+
promiseValueShadowCache.set(source, shadowed);
|
|
3203
|
+
return shadowed;
|
|
3204
|
+
}
|
|
3205
|
+
function declaresPromiseValue(node) {
|
|
3206
|
+
if (ts8.isVariableDeclaration(node) || ts8.isParameter(node))
|
|
3207
|
+
return bindingNameIsPromise(node.name);
|
|
3208
|
+
if (ts8.isImportClause(node))
|
|
3209
|
+
return !node.isTypeOnly && nodeIsPromise(node.name);
|
|
3210
|
+
if (ts8.isImportSpecifier(node))
|
|
3211
|
+
return !node.isTypeOnly && nodeIsPromise(node.name);
|
|
3212
|
+
if (ts8.isNamespaceImport(node) || ts8.isImportEqualsDeclaration(node))
|
|
3213
|
+
return nodeIsPromise(node.name);
|
|
3214
|
+
if (ts8.isFunctionDeclaration(node) || ts8.isClassDeclaration(node) || ts8.isEnumDeclaration(node) || ts8.isModuleDeclaration(node))
|
|
3215
|
+
return nodeIsPromise(node.name);
|
|
3216
|
+
return false;
|
|
3217
|
+
}
|
|
3218
|
+
function bindingNameIsPromise(name) {
|
|
3219
|
+
if (ts8.isIdentifier(name)) return name.text === "Promise";
|
|
3220
|
+
if (ts8.isObjectBindingPattern(name))
|
|
3221
|
+
return name.elements.some((element) => bindingNameIsPromise(element.name));
|
|
3222
|
+
return name.elements.some((element) => ts8.isBindingElement(element) && bindingNameIsPromise(element.name));
|
|
3223
|
+
}
|
|
3224
|
+
function nodeIsPromise(name) {
|
|
3225
|
+
return Boolean(name && ts8.isIdentifier(name) && name.text === "Promise");
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3022
3228
|
// src/parsers/outbound-call-parser.ts
|
|
3023
3229
|
function lineOf4(text, idx) {
|
|
3024
3230
|
return text.slice(0, idx).split("\n").length;
|
|
3025
3231
|
}
|
|
3026
3232
|
function entityFromExpression(expr) {
|
|
3027
3233
|
if (!expr) return void 0;
|
|
3028
|
-
if (
|
|
3029
|
-
if (
|
|
3030
|
-
if (
|
|
3234
|
+
if (ts9.isIdentifier(expr) || ts9.isStringLiteral(expr) || ts9.isNoSubstitutionTemplateLiteral(expr)) return expr.text;
|
|
3235
|
+
if (ts9.isPropertyAccessExpression(expr) && expr.expression.kind === ts9.SyntaxKind.ThisKeyword) return expr.name.text;
|
|
3236
|
+
if (ts9.isElementAccessExpression(expr) && expr.argumentExpression && (ts9.isStringLiteral(expr.argumentExpression) || ts9.isNoSubstitutionTemplateLiteral(expr.argumentExpression))) return expr.argumentExpression.text;
|
|
3031
3237
|
return void 0;
|
|
3032
3238
|
}
|
|
3033
|
-
function
|
|
3034
|
-
if (
|
|
3035
|
-
if (
|
|
3239
|
+
function expressionName2(expr) {
|
|
3240
|
+
if (ts9.isIdentifier(expr)) return expr.text;
|
|
3241
|
+
if (ts9.isPropertyAccessExpression(expr)) return `${expressionName2(expr.expression)}.${expr.name.text}`;
|
|
3036
3242
|
return expr.getText();
|
|
3037
3243
|
}
|
|
3038
3244
|
function variableInitializers(source) {
|
|
3039
3245
|
const initializers = /* @__PURE__ */ new Map();
|
|
3040
3246
|
for (const statement of source.statements) {
|
|
3041
|
-
if (!
|
|
3247
|
+
if (!ts9.isVariableStatement(statement) || (statement.declarationList.flags & ts9.NodeFlags.Const) === 0) continue;
|
|
3042
3248
|
for (const declaration of statement.declarationList.declarations) {
|
|
3043
|
-
if (
|
|
3249
|
+
if (ts9.isIdentifier(declaration.name) && declaration.initializer) initializers.set(declaration.name.text, declaration.initializer);
|
|
3044
3250
|
}
|
|
3045
3251
|
}
|
|
3046
3252
|
return initializers;
|
|
3047
3253
|
}
|
|
3254
|
+
function unwrapQueryExpression2(expr) {
|
|
3255
|
+
if (ts9.isParenthesizedExpression(expr) || ts9.isAwaitExpression(expr) || ts9.isAsExpression(expr) || ts9.isTypeAssertionExpression(expr) || ts9.isNonNullExpression(expr) || ts9.isSatisfiesExpression(expr))
|
|
3256
|
+
return unwrapQueryExpression2(expr.expression);
|
|
3257
|
+
return expr;
|
|
3258
|
+
}
|
|
3048
3259
|
function queryEntityFromAst(expr, initializers = /* @__PURE__ */ new Map()) {
|
|
3049
|
-
|
|
3050
|
-
if (
|
|
3051
|
-
if (
|
|
3052
|
-
const name =
|
|
3053
|
-
if (name === "cds.run") return queryEntityFromAst(
|
|
3054
|
-
if (
|
|
3055
|
-
|
|
3056
|
-
const receiver = ts8.isPropertyAccessExpression(expr.expression) ? expr.expression.expression : void 0;
|
|
3260
|
+
const unwrapped = unwrapQueryExpression2(expr);
|
|
3261
|
+
if (ts9.isIdentifier(unwrapped) && initializers.has(unwrapped.text)) return queryEntityFromAst(initializers.get(unwrapped.text), initializers);
|
|
3262
|
+
if (ts9.isCallExpression(unwrapped)) {
|
|
3263
|
+
const name = expressionName2(unwrapped.expression);
|
|
3264
|
+
if (name === "cds.run") return queryEntityFromAst(unwrapped.arguments[0], initializers);
|
|
3265
|
+
if (isCapQueryBuilderRootName(name)) return entityFromExpression(unwrapped.arguments[0]);
|
|
3266
|
+
const receiver = ts9.isPropertyAccessExpression(unwrapped.expression) ? unwrapped.expression.expression : void 0;
|
|
3057
3267
|
if (receiver) return queryEntityFromAst(receiver, initializers);
|
|
3058
3268
|
}
|
|
3059
3269
|
return void 0;
|
|
3060
3270
|
}
|
|
3271
|
+
function queryBuilderEvidence(source, statement) {
|
|
3272
|
+
return {
|
|
3273
|
+
classifier: "cap_query_builder_direct",
|
|
3274
|
+
queryDispatch: "direct_query_builder",
|
|
3275
|
+
queryRoot: expressionName2(statement.root.expression),
|
|
3276
|
+
queryRootStartOffset: statement.root.getStart(source),
|
|
3277
|
+
queryRootEndOffset: statement.root.getEnd(),
|
|
3278
|
+
queryStatementStartOffset: statement.statement.getStart(source),
|
|
3279
|
+
queryStatementEndOffset: statement.statement.getEnd(),
|
|
3280
|
+
queryExecutionContext: statement.executionContext
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
function queryRunEvidence(source, argument) {
|
|
3284
|
+
const root = argument ? queryBuilderRoot(argument) : void 0;
|
|
3285
|
+
return {
|
|
3286
|
+
classifier: "cap_query_run_wrapper",
|
|
3287
|
+
queryDispatch: "cds_run_wrapper",
|
|
3288
|
+
...root ? {
|
|
3289
|
+
queryRoot: expressionName2(root.expression),
|
|
3290
|
+
queryRootStartOffset: root.getStart(source),
|
|
3291
|
+
queryRootEndOffset: root.getEnd()
|
|
3292
|
+
} : {}
|
|
3293
|
+
};
|
|
3294
|
+
}
|
|
3061
3295
|
function extractQueryEntity(expr) {
|
|
3062
|
-
const source =
|
|
3296
|
+
const source = ts9.createSourceFile("query.ts", `const __query = (${expr});`, ts9.ScriptTarget.Latest, true, ts9.ScriptKind.TS);
|
|
3063
3297
|
const initializers = variableInitializers(source);
|
|
3064
3298
|
let found;
|
|
3065
3299
|
const visit = (node) => {
|
|
3066
3300
|
if (found) return;
|
|
3067
|
-
if (
|
|
3068
|
-
|
|
3301
|
+
if (ts9.isParenthesizedExpression(node)) found = queryEntityFromAst(node.expression, initializers);
|
|
3302
|
+
ts9.forEachChild(node, visit);
|
|
3069
3303
|
};
|
|
3070
3304
|
visit(source);
|
|
3071
3305
|
return found;
|
|
@@ -3079,7 +3313,7 @@ function parserEvidence(source, node, extra) {
|
|
|
3079
3313
|
return { parser: "typescript_ast", startOffset: node.getStart(source), endOffset: node.getEnd(), ...extra };
|
|
3080
3314
|
}
|
|
3081
3315
|
function isStringLike(expr) {
|
|
3082
|
-
return Boolean(expr && (
|
|
3316
|
+
return Boolean(expr && (ts9.isStringLiteral(expr) || ts9.isNoSubstitutionTemplateLiteral(expr)));
|
|
3083
3317
|
}
|
|
3084
3318
|
function literalText(expr) {
|
|
3085
3319
|
if (isStringLike(expr)) return expr.text;
|
|
@@ -3087,53 +3321,53 @@ function literalText(expr) {
|
|
|
3087
3321
|
}
|
|
3088
3322
|
function objectPropertyText(object, key) {
|
|
3089
3323
|
const prop = object.properties.find(
|
|
3090
|
-
(property) =>
|
|
3324
|
+
(property) => ts9.isPropertyAssignment(property) && nameOfProperty(property.name) === key || ts9.isShorthandPropertyAssignment(property) && property.name.text === key
|
|
3091
3325
|
);
|
|
3092
3326
|
if (!prop) return void 0;
|
|
3093
|
-
return
|
|
3327
|
+
return ts9.isShorthandPropertyAssignment(prop) ? prop.name.text : prop.initializer.getText();
|
|
3094
3328
|
}
|
|
3095
3329
|
function objectPropertyIsShorthand(object, key) {
|
|
3096
|
-
return object.properties.some((property) =>
|
|
3330
|
+
return object.properties.some((property) => ts9.isShorthandPropertyAssignment(property) && property.name.text === key);
|
|
3097
3331
|
}
|
|
3098
3332
|
function nameOfProperty(name) {
|
|
3099
|
-
if (
|
|
3333
|
+
if (ts9.isIdentifier(name) || ts9.isStringLiteral(name) || ts9.isNumericLiteral(name)) return name.text;
|
|
3100
3334
|
return void 0;
|
|
3101
3335
|
}
|
|
3102
3336
|
var maxAliasDepth2 = 5;
|
|
3103
3337
|
function safeRaw(expr) {
|
|
3104
|
-
if (
|
|
3338
|
+
if (ts9.isStringLiteral(expr) || ts9.isNoSubstitutionTemplateLiteral(expr) || ts9.isIdentifier(expr) || ts9.isTemplateExpression(expr)) return expr.getText(expr.getSourceFile());
|
|
3105
3339
|
return void 0;
|
|
3106
3340
|
}
|
|
3107
3341
|
function placeholders2(expr) {
|
|
3108
3342
|
return expr.templateSpans.map((span) => span.expression.getText(expr.getSourceFile()));
|
|
3109
3343
|
}
|
|
3110
3344
|
function isFunctionLikeScope(node) {
|
|
3111
|
-
return
|
|
3345
|
+
return ts9.isFunctionLike(node) || ts9.isSourceFile(node);
|
|
3112
3346
|
}
|
|
3113
3347
|
function nodeContains(parent, child) {
|
|
3114
3348
|
const source = child.getSourceFile();
|
|
3115
3349
|
return child.getStart(source) >= parent.getStart(source) && child.getEnd() <= parent.getEnd();
|
|
3116
3350
|
}
|
|
3117
3351
|
function declarationScope2(node) {
|
|
3118
|
-
if (
|
|
3119
|
-
if (
|
|
3352
|
+
if (ts9.isParameter(node)) return node.parent;
|
|
3353
|
+
if (ts9.isCatchClause(node.parent) && node.parent.variableDeclaration === node) return node.parent;
|
|
3120
3354
|
const list = node.parent;
|
|
3121
|
-
const blockScoped = (list.flags & (
|
|
3355
|
+
const blockScoped = (list.flags & (ts9.NodeFlags.Const | ts9.NodeFlags.Let)) !== 0;
|
|
3122
3356
|
let current = list.parent;
|
|
3123
3357
|
if (!blockScoped) {
|
|
3124
3358
|
while (current.parent && !isFunctionLikeScope(current)) current = current.parent;
|
|
3125
3359
|
return current;
|
|
3126
3360
|
}
|
|
3127
|
-
while (current.parent && !
|
|
3361
|
+
while (current.parent && !ts9.isBlock(current) && !ts9.isSourceFile(current) && !ts9.isModuleBlock(current) && !ts9.isCaseBlock(current) && !isLoopInitializerScope(node, current) && !isFunctionLikeScope(current)) current = current.parent;
|
|
3128
3362
|
return current;
|
|
3129
3363
|
}
|
|
3130
3364
|
function isLoopInitializerScope(declaration, scope) {
|
|
3131
3365
|
const list = declaration.parent;
|
|
3132
|
-
return
|
|
3366
|
+
return ts9.isForStatement(scope) && scope.initializer === list || (ts9.isForInStatement(scope) || ts9.isForOfStatement(scope)) && scope.initializer === list;
|
|
3133
3367
|
}
|
|
3134
3368
|
function catchBindingScope(declaration) {
|
|
3135
|
-
if (
|
|
3136
|
-
return
|
|
3369
|
+
if (ts9.isParameter(declaration)) return void 0;
|
|
3370
|
+
return ts9.isCatchClause(declaration.parent) && declaration.parent.variableDeclaration === declaration ? declaration.parent : void 0;
|
|
3137
3371
|
}
|
|
3138
3372
|
function isAccessibleDeclaration(declaration, use) {
|
|
3139
3373
|
const source = use.getSourceFile();
|
|
@@ -3141,32 +3375,32 @@ function isAccessibleDeclaration(declaration, use) {
|
|
|
3141
3375
|
const catchScope = catchBindingScope(declaration);
|
|
3142
3376
|
if (catchScope) return nodeContains(catchScope.block, use);
|
|
3143
3377
|
const scope = declarationScope2(declaration);
|
|
3144
|
-
if (
|
|
3145
|
-
return
|
|
3378
|
+
if (ts9.isForStatement(scope) || ts9.isForInStatement(scope) || ts9.isForOfStatement(scope)) return nodeContains(scope.statement, use);
|
|
3379
|
+
return ts9.isSourceFile(scope) || nodeContains(scope, use);
|
|
3146
3380
|
}
|
|
3147
3381
|
function resolveBinding2(identifier, use) {
|
|
3148
3382
|
const source = use.getSourceFile();
|
|
3149
3383
|
let best;
|
|
3150
3384
|
const visit = (node) => {
|
|
3151
|
-
if (
|
|
3152
|
-
if (
|
|
3153
|
-
|
|
3385
|
+
if (ts9.isVariableDeclaration(node) && ts9.isIdentifier(node.name) && node.name.text === identifier.text && isAccessibleDeclaration(node, use)) best = node;
|
|
3386
|
+
if (ts9.isParameter(node) && ts9.isIdentifier(node.name) && node.name.text === identifier.text && isAccessibleDeclaration(node, use)) best = node;
|
|
3387
|
+
ts9.forEachChild(node, visit);
|
|
3154
3388
|
};
|
|
3155
3389
|
visit(source);
|
|
3156
3390
|
if (!best) return { immutable: false, evidence: ["binding_not_found"] };
|
|
3157
|
-
const immutable =
|
|
3158
|
-
return { declaration: best, initializer:
|
|
3391
|
+
const immutable = ts9.isVariableDeclaration(best) && (best.parent.flags & ts9.NodeFlags.Const) !== 0;
|
|
3392
|
+
return { declaration: best, initializer: ts9.isVariableDeclaration(best) ? best.initializer : void 0, immutable, evidence: [immutable ? "lexical_const_binding_before_use" : "lexical_mutable_or_parameter_binding"] };
|
|
3159
3393
|
}
|
|
3160
3394
|
function resolveExpression(expr, use, policy, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
3161
3395
|
if (!expr) return { status: "unknown", sourceKind: "dynamic_expression", placeholderKeys: [], evidence: ["expression_missing"] };
|
|
3162
|
-
if (
|
|
3163
|
-
if (
|
|
3164
|
-
if (
|
|
3396
|
+
if (ts9.isStringLiteral(expr)) return { status: "static", sourceKind: "string_literal", value: expr.text, rawExpression: safeRaw(expr), placeholderKeys: [], evidence: ["string_literal"] };
|
|
3397
|
+
if (ts9.isNoSubstitutionTemplateLiteral(expr)) return { status: "static", sourceKind: "no_substitution_template", value: expr.text, rawExpression: safeRaw(expr), placeholderKeys: [], evidence: ["no_substitution_template"] };
|
|
3398
|
+
if (ts9.isTemplateExpression(expr)) {
|
|
3165
3399
|
const keys = placeholders2(expr);
|
|
3166
3400
|
if (policy === "operation_path") return { status: "dynamic", sourceKind: "template_with_substitutions", value: stripQuotes(expr.getText(expr.getSourceFile())), rawExpression: safeRaw(expr), placeholderKeys: keys, evidence: ["operation_path_template_placeholders_retained"] };
|
|
3167
3401
|
return { status: "dynamic", sourceKind: "template_with_substitutions", placeholderKeys: keys, evidence: ["template_substitutions_not_static_external_target"] };
|
|
3168
3402
|
}
|
|
3169
|
-
if (
|
|
3403
|
+
if (ts9.isIdentifier(expr)) {
|
|
3170
3404
|
if (depth >= maxAliasDepth2) return { status: "unknown", sourceKind: "const_alias", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: ["alias_depth_exceeded"], constName: expr.text };
|
|
3171
3405
|
const binding = resolveBinding2(expr, use);
|
|
3172
3406
|
if (!binding.declaration || !binding.initializer || !binding.immutable) return { status: "dynamic", sourceKind: "dynamic_expression", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: binding.evidence, constName: expr.text };
|
|
@@ -3175,12 +3409,12 @@ function resolveExpression(expr, use, policy, depth = 0, seen = /* @__PURE__ */
|
|
|
3175
3409
|
const resolved3 = resolveExpression(binding.initializer, binding.declaration, policy, depth + 1, seen);
|
|
3176
3410
|
return { ...resolved3, sourceKind: "const_alias", rawExpression: safeRaw(expr), constName: expr.text, evidence: [...binding.evidence, ...resolved3.evidence] };
|
|
3177
3411
|
}
|
|
3178
|
-
return { status: "dynamic", sourceKind: "dynamic_expression", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: [`unsupported_${
|
|
3412
|
+
return { status: "dynamic", sourceKind: "dynamic_expression", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: [`unsupported_${ts9.SyntaxKind[expr.kind] ?? "expression"}`] };
|
|
3179
3413
|
}
|
|
3180
3414
|
function staticExpressionText(expr, initializers) {
|
|
3181
3415
|
if (!expr) return void 0;
|
|
3182
3416
|
if (isStringLike(expr)) return expr.text;
|
|
3183
|
-
if (
|
|
3417
|
+
if (ts9.isIdentifier(expr) && initializers.has(expr.text)) return staticExpressionText(initializers.get(expr.text), initializers);
|
|
3184
3418
|
return void 0;
|
|
3185
3419
|
}
|
|
3186
3420
|
function operationPathFromStatic(text) {
|
|
@@ -3188,17 +3422,17 @@ function operationPathFromStatic(text) {
|
|
|
3188
3422
|
}
|
|
3189
3423
|
function destinationExpressionShape(expr) {
|
|
3190
3424
|
if (!expr) return void 0;
|
|
3191
|
-
if (
|
|
3192
|
-
if (
|
|
3193
|
-
if (
|
|
3194
|
-
if (
|
|
3195
|
-
if (
|
|
3196
|
-
if (
|
|
3197
|
-
return
|
|
3425
|
+
if (ts9.isIdentifier(expr)) return "identifier";
|
|
3426
|
+
if (ts9.isPropertyAccessExpression(expr) || ts9.isElementAccessExpression(expr)) return "property_read";
|
|
3427
|
+
if (ts9.isCallExpression(expr)) return "function_call";
|
|
3428
|
+
if (ts9.isConditionalExpression(expr)) return "conditional";
|
|
3429
|
+
if (ts9.isBinaryExpression(expr)) return "binary_expression";
|
|
3430
|
+
if (ts9.isTemplateExpression(expr)) return "template_expression";
|
|
3431
|
+
return ts9.SyntaxKind[expr.kind] ?? "expression";
|
|
3198
3432
|
}
|
|
3199
3433
|
function staticConditionalCandidates(expr, initializers) {
|
|
3200
|
-
const resolved3 = expr &&
|
|
3201
|
-
if (!resolved3 || !
|
|
3434
|
+
const resolved3 = expr && ts9.isIdentifier(expr) && initializers.has(expr.text) ? initializers.get(expr.text) : expr;
|
|
3435
|
+
if (!resolved3 || !ts9.isConditionalExpression(resolved3)) return void 0;
|
|
3202
3436
|
const left = staticExpressionText(resolved3.whenTrue, initializers);
|
|
3203
3437
|
const right = staticExpressionText(resolved3.whenFalse, initializers);
|
|
3204
3438
|
if (!left || !right) return void 0;
|
|
@@ -3206,8 +3440,8 @@ function staticConditionalCandidates(expr, initializers) {
|
|
|
3206
3440
|
}
|
|
3207
3441
|
function propertyInitializer(object, key) {
|
|
3208
3442
|
for (const property of object.properties) {
|
|
3209
|
-
if (
|
|
3210
|
-
if (
|
|
3443
|
+
if (ts9.isPropertyAssignment(property) && nameOfProperty(property.name) === key) return property.initializer;
|
|
3444
|
+
if (ts9.isShorthandPropertyAssignment(property) && property.name.text === key) return property.name;
|
|
3211
3445
|
}
|
|
3212
3446
|
return void 0;
|
|
3213
3447
|
}
|
|
@@ -3268,7 +3502,7 @@ function externalHttpEvidence(node, source) {
|
|
|
3268
3502
|
const exprText = expr.getText(source);
|
|
3269
3503
|
if (exprText === "useOrFetchDestination") {
|
|
3270
3504
|
const objectArg = node.arguments[0];
|
|
3271
|
-
if (objectArg &&
|
|
3505
|
+
if (objectArg && ts9.isObjectLiteralExpression(objectArg)) {
|
|
3272
3506
|
const destination = destinationTargetFromExpression(propertyInitializer(objectArg, "destinationName"), node);
|
|
3273
3507
|
return { externalTarget: destination ?? { kind: "unknown", dynamic: false }, classifier: "sap_destination_lookup", sourceCallShape: "useOrFetchDestination" };
|
|
3274
3508
|
}
|
|
@@ -3276,13 +3510,13 @@ function externalHttpEvidence(node, source) {
|
|
|
3276
3510
|
if (exprText === "executeHttpRequest") {
|
|
3277
3511
|
const destination = destinationTargetFromExpression(node.arguments[0], node);
|
|
3278
3512
|
const config = node.arguments[1];
|
|
3279
|
-
const method = config &&
|
|
3280
|
-
const url = config &&
|
|
3513
|
+
const method = config && ts9.isObjectLiteralExpression(config) ? httpMethodFromObject(config, node) : void 0;
|
|
3514
|
+
const url = config && ts9.isObjectLiteralExpression(config) ? urlTargetFromExpression(propertyInitializer(config, "url"), node) : { kind: "unknown", dynamic: false };
|
|
3281
3515
|
return { method, externalTarget: destination ? { ...url, destination } : url, classifier: "sap_execute_http_request", sourceCallShape: "executeHttpRequest" };
|
|
3282
3516
|
}
|
|
3283
3517
|
if (exprText === "axios") {
|
|
3284
3518
|
const config = node.arguments[0];
|
|
3285
|
-
if (config &&
|
|
3519
|
+
if (config && ts9.isObjectLiteralExpression(config)) {
|
|
3286
3520
|
const method = httpMethodFromObject(config, node);
|
|
3287
3521
|
return { method, externalTarget: urlTargetFromExpression(propertyInitializer(config, "url"), node), classifier: "axios_config_call", sourceCallShape: "axios(config)" };
|
|
3288
3522
|
}
|
|
@@ -3290,10 +3524,10 @@ function externalHttpEvidence(node, source) {
|
|
|
3290
3524
|
}
|
|
3291
3525
|
if (exprText === "fetch") {
|
|
3292
3526
|
const init = node.arguments[1];
|
|
3293
|
-
const method = init &&
|
|
3527
|
+
const method = init && ts9.isObjectLiteralExpression(init) ? httpMethodFromObject(init, node) : void 0;
|
|
3294
3528
|
return { method, externalTarget: urlTargetFromExpression(node.arguments[0], node), classifier: "fetch_call", sourceCallShape: "fetch" };
|
|
3295
3529
|
}
|
|
3296
|
-
if (
|
|
3530
|
+
if (ts9.isPropertyAccessExpression(expr) && ["get", "post", "put", "patch", "delete", "head"].includes(expr.name.text) && expr.expression.getText(source) === "axios") {
|
|
3297
3531
|
return { method: expr.name.text.toUpperCase(), externalTarget: urlTargetFromExpression(node.arguments[0], node), classifier: "axios_member_call", sourceCallShape: `axios.${expr.name.text}` };
|
|
3298
3532
|
}
|
|
3299
3533
|
return void 0;
|
|
@@ -3301,27 +3535,27 @@ function externalHttpEvidence(node, source) {
|
|
|
3301
3535
|
function collectServiceVariables(source) {
|
|
3302
3536
|
const vars = /* @__PURE__ */ new Set(["cds", "messaging", "messageClient", "eventClient"]);
|
|
3303
3537
|
const visit = (node) => {
|
|
3304
|
-
if (
|
|
3538
|
+
if (ts9.isVariableDeclaration(node) && ts9.isIdentifier(node.name) && node.initializer) {
|
|
3305
3539
|
const text = node.initializer.getText(source);
|
|
3306
3540
|
if (/cds\.connect\.(to|messaging)\s*\(/.test(text)) vars.add(node.name.text);
|
|
3307
3541
|
}
|
|
3308
|
-
|
|
3542
|
+
ts9.forEachChild(node, visit);
|
|
3309
3543
|
};
|
|
3310
3544
|
visit(source);
|
|
3311
3545
|
return vars;
|
|
3312
3546
|
}
|
|
3313
3547
|
function receiverName(expr) {
|
|
3314
|
-
if (
|
|
3315
|
-
if (
|
|
3548
|
+
if (ts9.isIdentifier(expr)) return expr.text;
|
|
3549
|
+
if (ts9.isPropertyAccessExpression(expr)) return expr.getText(sourceOf(expr));
|
|
3316
3550
|
return void 0;
|
|
3317
3551
|
}
|
|
3318
3552
|
function sourceOf(node) {
|
|
3319
3553
|
return node.getSourceFile();
|
|
3320
3554
|
}
|
|
3321
3555
|
function rootReceiverName(expr) {
|
|
3322
|
-
if (
|
|
3323
|
-
if (
|
|
3324
|
-
if (
|
|
3556
|
+
if (ts9.isIdentifier(expr)) return expr.text;
|
|
3557
|
+
if (ts9.isPropertyAccessExpression(expr)) return rootReceiverName(expr.expression);
|
|
3558
|
+
if (ts9.isCallExpression(expr)) return rootReceiverName(expr.expression);
|
|
3325
3559
|
return void 0;
|
|
3326
3560
|
}
|
|
3327
3561
|
function isSupportedEventReceiver(receiver, rootReceiver, serviceVariables) {
|
|
@@ -3338,38 +3572,38 @@ function collectWrapperSpecs(source) {
|
|
|
3338
3572
|
const serviceVariables = collectServiceVariables(source);
|
|
3339
3573
|
const calledNames = /* @__PURE__ */ new Set();
|
|
3340
3574
|
const collectCalls = (node) => {
|
|
3341
|
-
if (
|
|
3575
|
+
if (ts9.isCallExpression(node) && ts9.isIdentifier(node.expression))
|
|
3342
3576
|
calledNames.add(node.expression.text);
|
|
3343
|
-
if (
|
|
3577
|
+
if (ts9.isCallExpression(node) && ts9.isCallExpression(node.expression) && ts9.isIdentifier(node.expression.expression))
|
|
3344
3578
|
calledNames.add(node.expression.expression.text);
|
|
3345
|
-
|
|
3579
|
+
ts9.forEachChild(node, collectCalls);
|
|
3346
3580
|
};
|
|
3347
3581
|
collectCalls(source);
|
|
3348
3582
|
const scanFunction = (name, fn) => {
|
|
3349
3583
|
if (!calledNames.has(name) && !isExportedWrapper(fn)) return;
|
|
3350
|
-
const params = fn.parameters.map((param) =>
|
|
3584
|
+
const params = fn.parameters.map((param) => ts9.isIdentifier(param.name) ? param.name.text : void 0);
|
|
3351
3585
|
const sends = [];
|
|
3352
3586
|
const visit = (node) => {
|
|
3353
|
-
if (
|
|
3587
|
+
if (ts9.isCallExpression(node) && ts9.isPropertyAccessExpression(node.expression) && node.expression.name.text === "send" && ts9.isIdentifier(node.expression.expression)) {
|
|
3354
3588
|
const objectArg = node.arguments[0];
|
|
3355
|
-
if (objectArg &&
|
|
3589
|
+
if (objectArg && ts9.isObjectLiteralExpression(objectArg)) {
|
|
3356
3590
|
const pathProp = propertyInitializer(objectArg, "path");
|
|
3357
3591
|
const methodProp = propertyInitializer(objectArg, "method");
|
|
3358
|
-
const pathName = pathProp &&
|
|
3359
|
-
const methodName2 = methodProp &&
|
|
3592
|
+
const pathName = pathProp && ts9.isIdentifier(pathProp) ? pathProp.text : void 0;
|
|
3593
|
+
const methodName2 = methodProp && ts9.isIdentifier(methodProp) ? methodProp.text : void 0;
|
|
3360
3594
|
const methodLiteral = resolveExpression(methodProp, node, "literal").value;
|
|
3361
3595
|
if (pathName) sends.push({ client: node.expression.expression.text, path: pathName, method: methodName2, methodLiteral, start: node.getStart(source), end: node.getEnd() });
|
|
3362
3596
|
}
|
|
3363
3597
|
}
|
|
3364
|
-
if (
|
|
3598
|
+
if (ts9.isCallExpression(node) && ts9.isIdentifier(node.expression) && specs.has(node.expression.text)) {
|
|
3365
3599
|
const nested = specs.get(node.expression.text);
|
|
3366
3600
|
const pathArg = nested ? node.arguments[nested.pathIndex] : void 0;
|
|
3367
3601
|
const clientArg = nested?.clientIndex === void 0 ? void 0 : node.arguments[nested.clientIndex];
|
|
3368
|
-
const pathName = pathArg &&
|
|
3369
|
-
const clientName = clientArg &&
|
|
3602
|
+
const pathName = pathArg && ts9.isIdentifier(pathArg) ? pathArg.text : void 0;
|
|
3603
|
+
const clientName = clientArg && ts9.isIdentifier(clientArg) ? clientArg.text : nested?.clientName;
|
|
3370
3604
|
if (nested && pathName && clientName) sends.push({ client: clientName, path: pathName, method: nested.methodName, methodLiteral: nested.methodLiteral, nestedWrapperFunction: node.expression.text, start: node.getStart(source), end: node.getEnd() });
|
|
3371
3605
|
}
|
|
3372
|
-
|
|
3606
|
+
ts9.forEachChild(node, visit);
|
|
3373
3607
|
};
|
|
3374
3608
|
visit(fn);
|
|
3375
3609
|
if (sends.length !== 1) return;
|
|
@@ -3381,17 +3615,17 @@ function collectWrapperSpecs(source) {
|
|
|
3381
3615
|
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, nestedWrapperFunction: found.nestedWrapperFunction, definitionLine: lineOf4(source.text, fn.getStart(source)), internalStart: found.start, internalEnd: found.end });
|
|
3382
3616
|
};
|
|
3383
3617
|
const visitTop = (node) => {
|
|
3384
|
-
if (
|
|
3385
|
-
if (
|
|
3386
|
-
|
|
3618
|
+
if (ts9.isFunctionDeclaration(node) && node.name) scanFunction(node.name.text, node);
|
|
3619
|
+
if (ts9.isVariableDeclaration(node) && ts9.isIdentifier(node.name) && node.initializer && (ts9.isArrowFunction(node.initializer) || ts9.isFunctionExpression(node.initializer))) scanFunction(node.name.text, node.initializer);
|
|
3620
|
+
ts9.forEachChild(node, visitTop);
|
|
3387
3621
|
};
|
|
3388
3622
|
visitTop(source);
|
|
3389
3623
|
return specs;
|
|
3390
3624
|
}
|
|
3391
3625
|
function isExportedWrapper(fn) {
|
|
3392
|
-
const declaration =
|
|
3393
|
-
if (!declaration || !
|
|
3394
|
-
return
|
|
3626
|
+
const declaration = ts9.isFunctionDeclaration(fn) ? fn : ts9.isVariableDeclaration(fn.parent) ? fn.parent.parent.parent : void 0;
|
|
3627
|
+
if (!declaration || !ts9.canHaveModifiers(declaration)) return false;
|
|
3628
|
+
return ts9.getModifiers(declaration)?.some((modifier) => modifier.kind === ts9.SyntaxKind.ExportKeyword) ?? false;
|
|
3395
3629
|
}
|
|
3396
3630
|
function classifyOutboundCallsInSource(source, filePath) {
|
|
3397
3631
|
const calls = [];
|
|
@@ -3404,20 +3638,25 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3404
3638
|
calls.push({ node, fact: { ...fact, sourceFile, sourceLine: lineOf4(source.text, node.getStart(source)), confidence: fact.confidence ?? 0.8, evidence: parserEvidence(source, node, extra) } });
|
|
3405
3639
|
};
|
|
3406
3640
|
const visit = (node) => {
|
|
3407
|
-
if (
|
|
3641
|
+
if (ts9.isCallExpression(node)) {
|
|
3408
3642
|
if (wrapperInternalRanges.some((range) => node.getStart(source) >= range.start && node.getEnd() <= range.end)) {
|
|
3409
3643
|
return;
|
|
3410
3644
|
}
|
|
3411
3645
|
const expr = node.expression;
|
|
3412
3646
|
const exprText = expr.getText(source);
|
|
3647
|
+
const directQuery = directQueryBuilderStatement(node);
|
|
3413
3648
|
if (exprText === "cds.run") {
|
|
3414
3649
|
const arg = node.arguments[0];
|
|
3415
3650
|
const entity = arg ? queryEntityFromAst(arg, initializers) : void 0;
|
|
3416
3651
|
const payload = arg?.getText(source) ?? "";
|
|
3417
|
-
add(node, { callType: "local_db_query", queryEntity: entity, payloadSummary: summarizeExpression(payload), confidence: entity ? 0.9 : 0.55, unresolvedReason: entity ? void 0 : queryWarning(payload) });
|
|
3418
|
-
} else if (
|
|
3652
|
+
add(node, { callType: "local_db_query", queryEntity: entity, payloadSummary: summarizeExpression(payload), confidence: entity ? 0.9 : 0.55, unresolvedReason: entity ? void 0 : queryWarning(payload) }, queryRunEvidence(source, arg));
|
|
3653
|
+
} else if (directQuery) {
|
|
3654
|
+
const entity = queryEntityFromAst(directQuery.logicalCall, initializers);
|
|
3655
|
+
const payload = directQuery.logicalCall.getText(source);
|
|
3656
|
+
add(directQuery.logicalCall, { callType: "local_db_query", queryEntity: entity, payloadSummary: summarizeExpression(payload), confidence: entity ? 0.9 : 0.55, unresolvedReason: entity ? void 0 : queryWarning(payload) }, queryBuilderEvidence(source, directQuery));
|
|
3657
|
+
} else if (ts9.isPropertyAccessExpression(expr) && expr.name.text === "send" && (ts9.isIdentifier(expr.expression) || ts9.isPropertyAccessExpression(expr.expression))) {
|
|
3419
3658
|
const objectArg = node.arguments[0];
|
|
3420
|
-
if (objectArg &&
|
|
3659
|
+
if (objectArg && ts9.isObjectLiteralExpression(objectArg)) {
|
|
3421
3660
|
const receiver = receiverName(expr.expression);
|
|
3422
3661
|
const query = objectPropertyText(objectArg, "query");
|
|
3423
3662
|
const method = stripQuotes(resolveExpression(propertyInitializer(objectArg, "method"), node, "literal").value ?? objectPropertyText(objectArg, "method") ?? "POST");
|
|
@@ -3450,14 +3689,14 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3450
3689
|
add(node, { callType: "remote_action", serviceVariableName: rootReceiver ?? receiver, operationPathExpr, payloadSummary: summarizeExpression(node.getText(source)), confidence: operationPathExpr ? 0.75 : 0.35, unresolvedReason: operationPathExpr ? void 0 : "unsupported_cap_send_signature" }, { receiver, rootReceiver, classifier: operationPathExpr ? "service_client_send_operation_event" : "service_client_send_unsupported_signature", rawOperationExpression: firstArg2.rawExpression, literalOperationSource: firstArg2.value ? firstArg2.sourceKind : void 0, parserWarning: operationPathExpr ? void 0 : "unsupported_cap_send_signature" });
|
|
3451
3690
|
}
|
|
3452
3691
|
}
|
|
3453
|
-
} else if (
|
|
3454
|
-
const wrapperName =
|
|
3455
|
-
const wrapperArgs =
|
|
3692
|
+
} else if (ts9.isCallExpression(expr) && ts9.isIdentifier(expr.expression) && wrapperSpecs.has(expr.expression.text) || ts9.isIdentifier(expr) && wrapperSpecs.has(expr.text)) {
|
|
3693
|
+
const wrapperName = ts9.isIdentifier(expr) ? expr.text : ts9.isCallExpression(expr) && ts9.isIdentifier(expr.expression) ? expr.expression.text : "";
|
|
3694
|
+
const wrapperArgs = ts9.isIdentifier(expr) ? node.arguments : ts9.isCallExpression(expr) ? expr.arguments : node.arguments;
|
|
3456
3695
|
const spec = wrapperSpecs.get(wrapperName);
|
|
3457
3696
|
const clientArg = spec?.clientIndex === void 0 ? void 0 : wrapperArgs[spec.clientIndex];
|
|
3458
3697
|
const pathArg = spec ? wrapperArgs[spec.pathIndex] : void 0;
|
|
3459
3698
|
const methodArg = spec?.methodIndex === void 0 ? void 0 : wrapperArgs[spec.methodIndex];
|
|
3460
|
-
const receiver = clientArg &&
|
|
3699
|
+
const receiver = clientArg && ts9.isIdentifier(clientArg) ? clientArg.text : spec?.clientName;
|
|
3461
3700
|
const method = stripQuotes(resolveExpression(methodArg, node, "literal").value ?? spec?.methodLiteral ?? "POST");
|
|
3462
3701
|
const pathAnalysis = analyzeOperationPath(pathArg, node, method);
|
|
3463
3702
|
const operationPathExpr = operationPathExpression(pathAnalysis);
|
|
@@ -3468,7 +3707,7 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3468
3707
|
} else if (spec && receiver) {
|
|
3469
3708
|
add(node, { callType: "remote_action", serviceVariableName: receiver, method, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.45, unresolvedReason }, { receiver, classifier: pathAnalysis.status === "ambiguous" ? "higher_order_wrapper_ambiguous_path" : "higher_order_wrapper_dynamic_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf4(source.text, node.getStart(source)), wrapperPathSourceKind: wrapperSourceKind(pathAnalysis.sourceKind), rawPathExpression: pathAnalysis.rawExpression, pathAnalysis, parserWarning: unresolvedReason });
|
|
3470
3709
|
}
|
|
3471
|
-
} else if (
|
|
3710
|
+
} else if (ts9.isPropertyAccessExpression(expr) && ["emit", "publish", "on"].includes(expr.name.text)) {
|
|
3472
3711
|
const receiver = receiverName(expr.expression);
|
|
3473
3712
|
const rootReceiver = rootReceiverName(expr.expression);
|
|
3474
3713
|
if (isSupportedEventReceiver(receiver, rootReceiver, serviceVariables)) {
|
|
@@ -3484,7 +3723,7 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3484
3723
|
}
|
|
3485
3724
|
}
|
|
3486
3725
|
}
|
|
3487
|
-
|
|
3726
|
+
ts9.forEachChild(node, visit);
|
|
3488
3727
|
};
|
|
3489
3728
|
visit(source);
|
|
3490
3729
|
return calls;
|
|
@@ -3498,12 +3737,12 @@ function containsSupportedOutboundCall(node) {
|
|
|
3498
3737
|
async function parseOutboundCalls(repoPath, filePath, context) {
|
|
3499
3738
|
const snapshot = context?.get(filePath);
|
|
3500
3739
|
const text = snapshot?.text ?? await fs8.readFile(path11.join(repoPath, filePath), "utf8");
|
|
3501
|
-
const source = snapshot?.sourceFile() ??
|
|
3740
|
+
const source = snapshot?.sourceFile() ?? ts9.createSourceFile(
|
|
3502
3741
|
filePath,
|
|
3503
3742
|
text,
|
|
3504
|
-
|
|
3743
|
+
ts9.ScriptTarget.Latest,
|
|
3505
3744
|
true,
|
|
3506
|
-
filePath.endsWith(".ts") ?
|
|
3745
|
+
filePath.endsWith(".ts") ? ts9.ScriptKind.TS : ts9.ScriptKind.JS
|
|
3507
3746
|
);
|
|
3508
3747
|
const bindingNames = new Set((await parseServiceBindings(
|
|
3509
3748
|
repoPath,
|
|
@@ -3519,21 +3758,21 @@ async function parseOutboundCalls(repoPath, filePath, context) {
|
|
|
3519
3758
|
);
|
|
3520
3759
|
return [...classifyOutboundCallsInSource(source, filePath).map((call) => call.fact), ...importedWrappers, ...parseLocalServiceCalls(text, filePath, source)];
|
|
3521
3760
|
}
|
|
3522
|
-
function parseLocalServiceCalls(text, filePath, source =
|
|
3761
|
+
function parseLocalServiceCalls(text, filePath, source = ts9.createSourceFile(
|
|
3523
3762
|
filePath,
|
|
3524
3763
|
text,
|
|
3525
|
-
|
|
3764
|
+
ts9.ScriptTarget.Latest,
|
|
3526
3765
|
true,
|
|
3527
|
-
filePath.endsWith(".ts") ?
|
|
3766
|
+
filePath.endsWith(".ts") ? ts9.ScriptKind.TS : ts9.ScriptKind.JS
|
|
3528
3767
|
)) {
|
|
3529
3768
|
const aliases = /* @__PURE__ */ new Map();
|
|
3530
3769
|
const calls = [];
|
|
3531
3770
|
const visit = (node) => {
|
|
3532
|
-
if (
|
|
3771
|
+
if (ts9.isVariableDeclaration(node) && ts9.isIdentifier(node.name) && node.initializer) {
|
|
3533
3772
|
const origin = serviceLookup(node.initializer, aliases);
|
|
3534
3773
|
if (origin) aliases.set(node.name.text, { ...origin, chain: [...origin.chain, node.name.text] });
|
|
3535
3774
|
}
|
|
3536
|
-
if (
|
|
3775
|
+
if (ts9.isCallExpression(node)) {
|
|
3537
3776
|
const parsed = serviceOperationCall(node, aliases);
|
|
3538
3777
|
if (parsed && parsed.operation !== "entities") calls.push({
|
|
3539
3778
|
callType: "local_service_call",
|
|
@@ -3556,20 +3795,20 @@ function parseLocalServiceCalls(text, filePath, source = ts8.createSourceFile(
|
|
|
3556
3795
|
})
|
|
3557
3796
|
});
|
|
3558
3797
|
}
|
|
3559
|
-
|
|
3798
|
+
ts9.forEachChild(node, visit);
|
|
3560
3799
|
};
|
|
3561
3800
|
visit(source);
|
|
3562
3801
|
return calls;
|
|
3563
3802
|
}
|
|
3564
3803
|
function serviceLookup(expr, aliases) {
|
|
3565
|
-
if (
|
|
3566
|
-
if (
|
|
3567
|
-
if (
|
|
3804
|
+
if (ts9.isIdentifier(expr)) return aliases.get(expr.text);
|
|
3805
|
+
if (ts9.isPropertyAccessExpression(expr) && expr.expression.getText() === "cds.services") return { service: expr.name.text, lookup: expr.getText(), chain: [expr.getText()] };
|
|
3806
|
+
if (ts9.isElementAccessExpression(expr) && expr.expression.getText() === "cds.services" && ts9.isStringLiteral(expr.argumentExpression)) return { service: expr.argumentExpression.text, lookup: expr.getText(), chain: [expr.getText()] };
|
|
3568
3807
|
return void 0;
|
|
3569
3808
|
}
|
|
3570
3809
|
function serviceOperationCall(node, aliases) {
|
|
3571
3810
|
const expr = node.expression;
|
|
3572
|
-
if (!
|
|
3811
|
+
if (!ts9.isPropertyAccessExpression(expr)) return void 0;
|
|
3573
3812
|
const origin = serviceLookup(expr.expression, aliases);
|
|
3574
3813
|
if (!origin) return void 0;
|
|
3575
3814
|
if (expr.name.text === "send") {
|
|
@@ -8250,4 +8489,4 @@ export {
|
|
|
8250
8489
|
selectorRepoAmbiguousDiagnostic,
|
|
8251
8490
|
trace
|
|
8252
8491
|
};
|
|
8253
|
-
//# sourceMappingURL=chunk-
|
|
8492
|
+
//# sourceMappingURL=chunk-Y7H7ZU5B.js.map
|