@saptools/service-flow 0.1.58 → 0.1.60
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 +11 -0
- package/dist/{chunk-CRSSXWQ2.js → chunk-BGD7UYJN.js} +433 -166
- package/dist/chunk-BGD7UYJN.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/parsers/000-direct-query-execution.ts +2 -0
- package/src/parsers/001-query-entity-resolution.ts +622 -0
- package/src/parsers/outbound-call-parser.ts +15 -109
- package/dist/chunk-CRSSXWQ2.js.map +0 -1
|
@@ -2358,7 +2358,7 @@ function collectClassHelpers(sf) {
|
|
|
2358
2358
|
// src/parsers/outbound-call-parser.ts
|
|
2359
2359
|
import fs8 from "fs/promises";
|
|
2360
2360
|
import path11 from "path";
|
|
2361
|
-
import
|
|
2361
|
+
import ts10 from "typescript";
|
|
2362
2362
|
|
|
2363
2363
|
// src/linker/external-http-target.ts
|
|
2364
2364
|
import { createHash } from "crypto";
|
|
@@ -3080,6 +3080,8 @@ var capQueryBuilderRoots = /* @__PURE__ */ new Set([
|
|
|
3080
3080
|
"SELECT.from",
|
|
3081
3081
|
"SELECT.one.from",
|
|
3082
3082
|
"SELECT.one",
|
|
3083
|
+
"SELECT.distinct.from",
|
|
3084
|
+
"SELECT.distinct.one.from",
|
|
3083
3085
|
"INSERT.into",
|
|
3084
3086
|
"UPSERT.into",
|
|
3085
3087
|
"UPDATE.entity",
|
|
@@ -3280,20 +3282,15 @@ function nodeIsPromise(name) {
|
|
|
3280
3282
|
return Boolean(name && ts8.isIdentifier(name) && name.text === "Promise");
|
|
3281
3283
|
}
|
|
3282
3284
|
|
|
3283
|
-
// src/parsers/
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
if (!expr) return void 0;
|
|
3289
|
-
if (ts9.isIdentifier(expr) || ts9.isStringLiteral(expr) || ts9.isNoSubstitutionTemplateLiteral(expr)) return expr.text;
|
|
3290
|
-
if (ts9.isPropertyAccessExpression(expr) && expr.expression.kind === ts9.SyntaxKind.ThisKeyword) return expr.name.text;
|
|
3291
|
-
if (ts9.isElementAccessExpression(expr) && expr.argumentExpression && (ts9.isStringLiteral(expr.argumentExpression) || ts9.isNoSubstitutionTemplateLiteral(expr.argumentExpression))) return expr.argumentExpression.text;
|
|
3292
|
-
return void 0;
|
|
3293
|
-
}
|
|
3285
|
+
// src/parsers/001-query-entity-resolution.ts
|
|
3286
|
+
import ts9 from "typescript";
|
|
3287
|
+
var maxAliasDepth2 = 5;
|
|
3288
|
+
var cdsModelSpecifier = /^#cds-models(?:\/|$)/;
|
|
3289
|
+
var modelNamespaceCache = /* @__PURE__ */ new WeakMap();
|
|
3294
3290
|
function expressionName2(expr) {
|
|
3295
3291
|
if (ts9.isIdentifier(expr)) return expr.text;
|
|
3296
|
-
if (ts9.isPropertyAccessExpression(expr))
|
|
3292
|
+
if (ts9.isPropertyAccessExpression(expr))
|
|
3293
|
+
return `${expressionName2(expr.expression)}.${expr.name.text}`;
|
|
3297
3294
|
return expr.getText();
|
|
3298
3295
|
}
|
|
3299
3296
|
function variableInitializers(source) {
|
|
@@ -3301,7 +3298,8 @@ function variableInitializers(source) {
|
|
|
3301
3298
|
for (const statement of source.statements) {
|
|
3302
3299
|
if (!ts9.isVariableStatement(statement) || (statement.declarationList.flags & ts9.NodeFlags.Const) === 0) continue;
|
|
3303
3300
|
for (const declaration of statement.declarationList.declarations) {
|
|
3304
|
-
if (ts9.isIdentifier(declaration.name) && declaration.initializer)
|
|
3301
|
+
if (ts9.isIdentifier(declaration.name) && declaration.initializer)
|
|
3302
|
+
initializers.set(declaration.name.text, declaration.initializer);
|
|
3305
3303
|
}
|
|
3306
3304
|
}
|
|
3307
3305
|
return initializers;
|
|
@@ -3311,18 +3309,349 @@ function unwrapQueryExpression2(expr) {
|
|
|
3311
3309
|
return unwrapQueryExpression2(expr.expression);
|
|
3312
3310
|
return expr;
|
|
3313
3311
|
}
|
|
3314
|
-
function
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3312
|
+
function isFunctionLikeScope(node) {
|
|
3313
|
+
return ts9.isFunctionLike(node) || ts9.isSourceFile(node);
|
|
3314
|
+
}
|
|
3315
|
+
function nodeContains(parent, child) {
|
|
3316
|
+
const source = child.getSourceFile();
|
|
3317
|
+
return child.getStart(source) >= parent.getStart(source) && child.getEnd() <= parent.getEnd();
|
|
3318
|
+
}
|
|
3319
|
+
function isLoopInitializerScope(declaration, scope) {
|
|
3320
|
+
const list = declaration.parent;
|
|
3321
|
+
return ts9.isForStatement(scope) && scope.initializer === list || (ts9.isForInStatement(scope) || ts9.isForOfStatement(scope)) && scope.initializer === list;
|
|
3322
|
+
}
|
|
3323
|
+
function isLexicalScopeBoundary(node, declaration) {
|
|
3324
|
+
return ts9.isBlock(node) || ts9.isSourceFile(node) || ts9.isModuleBlock(node) || ts9.isCaseBlock(node) || isLoopInitializerScope(declaration, node) || isFunctionLikeScope(node);
|
|
3325
|
+
}
|
|
3326
|
+
function declarationScope2(node) {
|
|
3327
|
+
if (ts9.isParameter(node)) return node.parent;
|
|
3328
|
+
if (ts9.isCatchClause(node.parent) && node.parent.variableDeclaration === node)
|
|
3329
|
+
return node.parent;
|
|
3330
|
+
const list = node.parent;
|
|
3331
|
+
const blockScoped = (list.flags & (ts9.NodeFlags.Const | ts9.NodeFlags.Let)) !== 0;
|
|
3332
|
+
let current = list.parent;
|
|
3333
|
+
if (!blockScoped) {
|
|
3334
|
+
while (current.parent && !isFunctionLikeScope(current)) current = current.parent;
|
|
3335
|
+
return current;
|
|
3336
|
+
}
|
|
3337
|
+
while (current.parent && !isLexicalScopeBoundary(current, node))
|
|
3338
|
+
current = current.parent;
|
|
3339
|
+
return current;
|
|
3340
|
+
}
|
|
3341
|
+
function catchBindingScope(declaration) {
|
|
3342
|
+
if (ts9.isParameter(declaration)) return void 0;
|
|
3343
|
+
return ts9.isCatchClause(declaration.parent) && declaration.parent.variableDeclaration === declaration ? declaration.parent : void 0;
|
|
3344
|
+
}
|
|
3345
|
+
function declarationIsInScope(declaration, use) {
|
|
3346
|
+
const catchScope = catchBindingScope(declaration);
|
|
3347
|
+
if (catchScope) return nodeContains(catchScope.block, use);
|
|
3348
|
+
const scope = declarationScope2(declaration);
|
|
3349
|
+
if (ts9.isForStatement(scope) || ts9.isForInStatement(scope) || ts9.isForOfStatement(scope)) return nodeContains(scope.statement, use);
|
|
3350
|
+
return ts9.isSourceFile(scope) || nodeContains(scope, use);
|
|
3351
|
+
}
|
|
3352
|
+
function isAccessibleDeclaration(declaration, use) {
|
|
3353
|
+
return declaration.name.getStart(use.getSourceFile()) < use.getStart() && declarationIsInScope(declaration, use);
|
|
3354
|
+
}
|
|
3355
|
+
function resolveBinding2(identifier, use) {
|
|
3356
|
+
const source = use.getSourceFile();
|
|
3357
|
+
let best;
|
|
3358
|
+
const visit = (node) => {
|
|
3359
|
+
if (ts9.isVariableDeclaration(node) && ts9.isIdentifier(node.name) && node.name.text === identifier.text && isAccessibleDeclaration(node, use))
|
|
3360
|
+
best = node;
|
|
3361
|
+
if (ts9.isParameter(node) && ts9.isIdentifier(node.name) && node.name.text === identifier.text && isAccessibleDeclaration(node, use))
|
|
3362
|
+
best = node;
|
|
3363
|
+
ts9.forEachChild(node, visit);
|
|
3364
|
+
};
|
|
3365
|
+
visit(source);
|
|
3366
|
+
if (!best) return { immutable: false, evidence: ["binding_not_found"] };
|
|
3367
|
+
const immutable = ts9.isVariableDeclaration(best) && (best.parent.flags & ts9.NodeFlags.Const) !== 0;
|
|
3368
|
+
return {
|
|
3369
|
+
declaration: best,
|
|
3370
|
+
initializer: ts9.isVariableDeclaration(best) ? best.initializer : void 0,
|
|
3371
|
+
immutable,
|
|
3372
|
+
evidence: [immutable ? "lexical_const_binding_before_use" : "lexical_mutable_or_parameter_binding"]
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
3375
|
+
function directBindingElement(name, identifier) {
|
|
3376
|
+
if (ts9.isIdentifier(name)) return void 0;
|
|
3377
|
+
for (const element of name.elements) {
|
|
3378
|
+
if (ts9.isOmittedExpression(element) || element.dotDotDotToken || element.initializer || !ts9.isIdentifier(element.name)) continue;
|
|
3379
|
+
if (element.name.text === identifier.text) return element;
|
|
3323
3380
|
}
|
|
3324
3381
|
return void 0;
|
|
3325
3382
|
}
|
|
3383
|
+
function bindingNameContains(name, target) {
|
|
3384
|
+
if (ts9.isIdentifier(name)) return name.text === target;
|
|
3385
|
+
return name.elements.some((element) => ts9.isBindingElement(element) && bindingNameContains(element.name, target));
|
|
3386
|
+
}
|
|
3387
|
+
function bindingScope(node) {
|
|
3388
|
+
if (ts9.isVariableDeclaration(node) || ts9.isParameter(node))
|
|
3389
|
+
return declarationScope2(node);
|
|
3390
|
+
if (ts9.isFunctionExpression(node) || ts9.isClassExpression(node)) return node;
|
|
3391
|
+
return node.parent;
|
|
3392
|
+
}
|
|
3393
|
+
function bindingIsCloser(candidate, current, use) {
|
|
3394
|
+
const source = use.getSourceFile();
|
|
3395
|
+
const candidateScope = bindingScope(candidate);
|
|
3396
|
+
const currentScope = bindingScope(current);
|
|
3397
|
+
const candidateSpan = candidateScope.getEnd() - candidateScope.getStart(source);
|
|
3398
|
+
const currentSpan = currentScope.getEnd() - currentScope.getStart(source);
|
|
3399
|
+
return candidateSpan < currentSpan || candidateSpan === currentSpan && candidate.getStart(source) > current.getStart(source);
|
|
3400
|
+
}
|
|
3401
|
+
function nearestScopedVariableDeclaration(identifier) {
|
|
3402
|
+
let best;
|
|
3403
|
+
const visit = (node) => {
|
|
3404
|
+
if ((ts9.isVariableDeclaration(node) || ts9.isParameter(node)) && bindingNameContains(node.name, identifier.text) && declarationIsInScope(node, identifier) && (!best || bindingIsCloser(node, best, identifier))) best = node;
|
|
3405
|
+
ts9.forEachChild(node, visit);
|
|
3406
|
+
};
|
|
3407
|
+
visit(identifier.getSourceFile());
|
|
3408
|
+
return best;
|
|
3409
|
+
}
|
|
3410
|
+
function hasScopedVariableDeclaration(identifier) {
|
|
3411
|
+
return Boolean(nearestScopedVariableDeclaration(identifier));
|
|
3412
|
+
}
|
|
3413
|
+
function namedValueDeclarationMatches(node, name) {
|
|
3414
|
+
if (ts9.isFunctionDeclaration(node) || ts9.isClassDeclaration(node) || ts9.isEnumDeclaration(node)) return node.name?.text === name;
|
|
3415
|
+
return ts9.isModuleDeclaration(node) && ts9.isIdentifier(node.name) && node.name.text === name;
|
|
3416
|
+
}
|
|
3417
|
+
function declarationContainerContains(node, use) {
|
|
3418
|
+
const container = node.parent;
|
|
3419
|
+
return ts9.isSourceFile(container) || (ts9.isBlock(container) || ts9.isModuleBlock(container)) && nodeContains(container, use);
|
|
3420
|
+
}
|
|
3421
|
+
function selfNamedValueDeclarationMatches(node, identifier) {
|
|
3422
|
+
return (ts9.isFunctionExpression(node) || ts9.isClassExpression(node)) && node.name?.text === identifier.text && nodeContains(node, identifier);
|
|
3423
|
+
}
|
|
3424
|
+
function scopedNonVariableDeclarationMatches(node, identifier) {
|
|
3425
|
+
return namedValueDeclarationMatches(node, identifier.text) && declarationContainerContains(node, identifier) || selfNamedValueDeclarationMatches(node, identifier);
|
|
3426
|
+
}
|
|
3427
|
+
function nearestScopedNonVariableDeclaration(identifier) {
|
|
3428
|
+
let best;
|
|
3429
|
+
const visit = (node) => {
|
|
3430
|
+
if (scopedNonVariableDeclarationMatches(node, identifier) && (!best || bindingIsCloser(node, best, identifier))) best = node;
|
|
3431
|
+
ts9.forEachChild(node, visit);
|
|
3432
|
+
};
|
|
3433
|
+
visit(identifier.getSourceFile());
|
|
3434
|
+
return best;
|
|
3435
|
+
}
|
|
3436
|
+
function hasScopedNonVariableDeclaration(identifier) {
|
|
3437
|
+
return Boolean(nearestScopedNonVariableDeclaration(identifier));
|
|
3438
|
+
}
|
|
3439
|
+
function nonVariableBindingWins(identifier, selected) {
|
|
3440
|
+
const declaration = nearestScopedNonVariableDeclaration(identifier);
|
|
3441
|
+
return Boolean(declaration && (!selected || bindingIsCloser(declaration, selected, identifier)));
|
|
3442
|
+
}
|
|
3443
|
+
function importClauseBinds(clause, name) {
|
|
3444
|
+
if (!clause || clause.isTypeOnly) return false;
|
|
3445
|
+
if (clause.name?.text === name) return true;
|
|
3446
|
+
const bindings = clause.namedBindings;
|
|
3447
|
+
if (!bindings) return false;
|
|
3448
|
+
if (ts9.isNamespaceImport(bindings)) return bindings.name.text === name;
|
|
3449
|
+
return bindings.elements.some((element) => !element.isTypeOnly && element.name.text === name);
|
|
3450
|
+
}
|
|
3451
|
+
function hasValueImportBinding(identifier) {
|
|
3452
|
+
return identifier.getSourceFile().statements.some((statement) => {
|
|
3453
|
+
if (ts9.isImportDeclaration(statement))
|
|
3454
|
+
return importClauseBinds(statement.importClause, identifier.text);
|
|
3455
|
+
return ts9.isImportEqualsDeclaration(statement) && !statement.isTypeOnly && statement.name.text === identifier.text;
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
function sourcePropertyName(element, localName) {
|
|
3459
|
+
const property = element.propertyName;
|
|
3460
|
+
if (!property) return localName;
|
|
3461
|
+
if (ts9.isIdentifier(property) || ts9.isStringLiteral(property) || ts9.isNumericLiteral(property)) return property.text;
|
|
3462
|
+
return void 0;
|
|
3463
|
+
}
|
|
3464
|
+
function resolveDestructuredBinding(identifier, use) {
|
|
3465
|
+
const source = use.getSourceFile();
|
|
3466
|
+
let best;
|
|
3467
|
+
const visit = (node) => {
|
|
3468
|
+
if ((ts9.isVariableDeclaration(node) || ts9.isParameter(node)) && isAccessibleDeclaration(node, use)) {
|
|
3469
|
+
const element = directBindingElement(node.name, identifier);
|
|
3470
|
+
const entityName = element ? sourcePropertyName(element, identifier.text) : void 0;
|
|
3471
|
+
if (entityName && (!best || node.name.getStart(source) > best.declaration.name.getStart(source))) {
|
|
3472
|
+
best = {
|
|
3473
|
+
declaration: node,
|
|
3474
|
+
initializer: node.initializer,
|
|
3475
|
+
entityName
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
ts9.forEachChild(node, visit);
|
|
3480
|
+
};
|
|
3481
|
+
visit(source);
|
|
3482
|
+
return best;
|
|
3483
|
+
}
|
|
3484
|
+
function isEntitiesBase(expr) {
|
|
3485
|
+
const value = unwrapQueryExpression2(expr);
|
|
3486
|
+
if (ts9.isPropertyAccessExpression(value)) return value.name.text === "entities";
|
|
3487
|
+
return ts9.isCallExpression(value) && ts9.isPropertyAccessExpression(value.expression) && value.expression.name.text === "entities";
|
|
3488
|
+
}
|
|
3489
|
+
function hasLexicalValueShadow(identifier, includeImports) {
|
|
3490
|
+
return hasScopedVariableDeclaration(identifier) || hasScopedNonVariableDeclaration(identifier) || includeImports && hasValueImportBinding(identifier);
|
|
3491
|
+
}
|
|
3492
|
+
function modelRequire(expr) {
|
|
3493
|
+
const value = unwrapQueryExpression2(expr);
|
|
3494
|
+
if (!ts9.isCallExpression(value) || !ts9.isIdentifier(value.expression) || value.expression.text !== "require" || value.arguments.length !== 1)
|
|
3495
|
+
return false;
|
|
3496
|
+
if (hasLexicalValueShadow(value.expression, true)) return false;
|
|
3497
|
+
const specifier = value.arguments[0];
|
|
3498
|
+
return Boolean(specifier && ts9.isStringLiteralLike(specifier) && cdsModelSpecifier.test(specifier.text));
|
|
3499
|
+
}
|
|
3500
|
+
function modelNamespaceImportName(statement) {
|
|
3501
|
+
if (!ts9.isImportDeclaration(statement) || !ts9.isStringLiteralLike(statement.moduleSpecifier) || !cdsModelSpecifier.test(statement.moduleSpecifier.text) || statement.importClause?.isTypeOnly) return void 0;
|
|
3502
|
+
const bindings = statement.importClause?.namedBindings;
|
|
3503
|
+
return bindings && ts9.isNamespaceImport(bindings) ? bindings.name.text : void 0;
|
|
3504
|
+
}
|
|
3505
|
+
function modelImportEqualsName(statement) {
|
|
3506
|
+
if (!ts9.isImportEqualsDeclaration(statement) || statement.isTypeOnly || !ts9.isExternalModuleReference(statement.moduleReference)) return void 0;
|
|
3507
|
+
const specifier = statement.moduleReference.expression;
|
|
3508
|
+
return specifier && ts9.isStringLiteralLike(specifier) && cdsModelSpecifier.test(specifier.text) ? statement.name.text : void 0;
|
|
3509
|
+
}
|
|
3510
|
+
function modelNamespaceNames(source) {
|
|
3511
|
+
const cached = modelNamespaceCache.get(source);
|
|
3512
|
+
if (cached) return cached;
|
|
3513
|
+
const names = /* @__PURE__ */ new Set();
|
|
3514
|
+
for (const statement of source.statements) {
|
|
3515
|
+
const name = modelNamespaceImportName(statement) ?? modelImportEqualsName(statement);
|
|
3516
|
+
if (name) names.add(name);
|
|
3517
|
+
}
|
|
3518
|
+
modelNamespaceCache.set(source, names);
|
|
3519
|
+
return names;
|
|
3520
|
+
}
|
|
3521
|
+
function destructuredEntitySource(initializer) {
|
|
3522
|
+
if (!initializer) return false;
|
|
3523
|
+
if (isEntitiesBase(initializer) || modelRequire(initializer)) return true;
|
|
3524
|
+
const value = unwrapQueryExpression2(initializer);
|
|
3525
|
+
return ts9.isIdentifier(value) && !hasLexicalValueShadow(value, false) && modelNamespaceNames(value.getSourceFile()).has(value.text);
|
|
3526
|
+
}
|
|
3527
|
+
function isAssignmentOperator(kind) {
|
|
3528
|
+
return kind >= ts9.SyntaxKind.FirstAssignment && kind <= ts9.SyntaxKind.LastAssignment;
|
|
3529
|
+
}
|
|
3530
|
+
function identifierIsReadWithinTarget(identifier, parent) {
|
|
3531
|
+
if (ts9.isPropertyAccessExpression(parent) && parent.name === identifier) return true;
|
|
3532
|
+
if (ts9.isElementAccessExpression(parent) && parent.argumentExpression && nodeContains(parent.argumentExpression, identifier)) return true;
|
|
3533
|
+
if (ts9.isPropertyAssignment(parent) && nodeContains(parent.name, identifier)) return true;
|
|
3534
|
+
return ts9.isComputedPropertyName(parent) && nodeContains(parent.expression, identifier);
|
|
3535
|
+
}
|
|
3536
|
+
function identifierIsUnaryWrite(identifier) {
|
|
3537
|
+
const direct = identifier.parent;
|
|
3538
|
+
if (!((ts9.isPrefixUnaryExpression(direct) || ts9.isPostfixUnaryExpression(direct)) && direct.operand === identifier)) return false;
|
|
3539
|
+
return direct.operator === ts9.SyntaxKind.PlusPlusToken || direct.operator === ts9.SyntaxKind.MinusMinusToken;
|
|
3540
|
+
}
|
|
3541
|
+
function identifierIsNestedWrite(identifier) {
|
|
3542
|
+
let current = identifier;
|
|
3543
|
+
while (current.parent) {
|
|
3544
|
+
const parent = current.parent;
|
|
3545
|
+
if (identifierIsReadWithinTarget(identifier, parent)) return false;
|
|
3546
|
+
if (ts9.isBinaryExpression(parent))
|
|
3547
|
+
return isAssignmentOperator(parent.operatorToken.kind) && nodeContains(parent.left, identifier);
|
|
3548
|
+
if (ts9.isForInStatement(parent) || ts9.isForOfStatement(parent))
|
|
3549
|
+
return nodeContains(parent.initializer, identifier);
|
|
3550
|
+
current = parent;
|
|
3551
|
+
}
|
|
3552
|
+
return false;
|
|
3553
|
+
}
|
|
3554
|
+
function identifierIsWrite(identifier) {
|
|
3555
|
+
return identifierIsUnaryWrite(identifier) || identifierIsNestedWrite(identifier);
|
|
3556
|
+
}
|
|
3557
|
+
function destructuredBindingWasWritten(binding, localName, use) {
|
|
3558
|
+
if (ts9.isParameter(binding.declaration)) return true;
|
|
3559
|
+
if ((binding.declaration.parent.flags & ts9.NodeFlags.Const) !== 0) return false;
|
|
3560
|
+
let written = false;
|
|
3561
|
+
const declarationEnd = binding.declaration.name.getEnd();
|
|
3562
|
+
const useStart = use.getStart();
|
|
3563
|
+
const visit = (node) => {
|
|
3564
|
+
if (written) return;
|
|
3565
|
+
if (ts9.isIdentifier(node) && node.text === localName && node.getStart() > declarationEnd && node.getStart() < useStart && identifierIsWrite(node) && resolveDestructuredBinding(node, node)?.declaration === binding.declaration)
|
|
3566
|
+
written = true;
|
|
3567
|
+
ts9.forEachChild(node, visit);
|
|
3568
|
+
};
|
|
3569
|
+
visit(use.getSourceFile());
|
|
3570
|
+
return written;
|
|
3571
|
+
}
|
|
3572
|
+
function destructuredBindingWins(destructured, simple, use) {
|
|
3573
|
+
return !simple.declaration || bindingIsCloser(destructured.declaration, simple.declaration, use);
|
|
3574
|
+
}
|
|
3575
|
+
function entityFromDestructuredBinding(binding, identifier) {
|
|
3576
|
+
if (destructuredBindingWasWritten(binding, identifier.text, identifier))
|
|
3577
|
+
return void 0;
|
|
3578
|
+
return destructuredEntitySource(binding.initializer) ? binding.entityName : void 0;
|
|
3579
|
+
}
|
|
3580
|
+
function entityFromSimpleBinding(binding, depth, seen) {
|
|
3581
|
+
if (!binding.declaration || !binding.immutable || !binding.initializer || seen.has(binding.declaration)) return void 0;
|
|
3582
|
+
seen.add(binding.declaration);
|
|
3583
|
+
return entityFromExpression(binding.initializer, depth + 1, seen);
|
|
3584
|
+
}
|
|
3585
|
+
function scopedBindingIsCurrent(identifier, declaration) {
|
|
3586
|
+
return nearestScopedVariableDeclaration(identifier) === declaration && !nonVariableBindingWins(identifier, declaration);
|
|
3587
|
+
}
|
|
3588
|
+
function unboundEntityName(identifier) {
|
|
3589
|
+
return hasScopedVariableDeclaration(identifier) || hasScopedNonVariableDeclaration(identifier) ? void 0 : identifier.text;
|
|
3590
|
+
}
|
|
3591
|
+
function entityFromIdentifier(expr, depth, seen) {
|
|
3592
|
+
const binding = resolveBinding2(expr, expr);
|
|
3593
|
+
const destructured = resolveDestructuredBinding(expr, expr);
|
|
3594
|
+
if (destructured && destructuredBindingWins(destructured, binding, expr)) {
|
|
3595
|
+
return scopedBindingIsCurrent(expr, destructured.declaration) ? entityFromDestructuredBinding(destructured, expr) : void 0;
|
|
3596
|
+
}
|
|
3597
|
+
if (!binding.declaration) return unboundEntityName(expr);
|
|
3598
|
+
if (!scopedBindingIsCurrent(expr, binding.declaration)) return void 0;
|
|
3599
|
+
return entityFromSimpleBinding(binding, depth, seen);
|
|
3600
|
+
}
|
|
3601
|
+
function literalEntity(expr) {
|
|
3602
|
+
if (ts9.isStringLiteral(expr) || ts9.isNoSubstitutionTemplateLiteral(expr))
|
|
3603
|
+
return expr.text;
|
|
3604
|
+
if (!ts9.isElementAccessExpression(expr) || !expr.argumentExpression)
|
|
3605
|
+
return void 0;
|
|
3606
|
+
return ts9.isStringLiteral(expr.argumentExpression) || ts9.isNoSubstitutionTemplateLiteral(expr.argumentExpression) ? expr.argumentExpression.text : void 0;
|
|
3607
|
+
}
|
|
3608
|
+
function propertyEntity(expr) {
|
|
3609
|
+
if (!ts9.isPropertyAccessExpression(expr)) return void 0;
|
|
3610
|
+
if (expr.expression.kind === ts9.SyntaxKind.ThisKeyword) return void 0;
|
|
3611
|
+
return isEntitiesBase(expr.expression) ? expr.name.text : void 0;
|
|
3612
|
+
}
|
|
3613
|
+
function entityFromExpression(expr, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
3614
|
+
if (!expr || depth >= maxAliasDepth2) return void 0;
|
|
3615
|
+
const value = unwrapQueryExpression2(expr);
|
|
3616
|
+
const literal2 = literalEntity(value);
|
|
3617
|
+
if (literal2 !== void 0) return literal2;
|
|
3618
|
+
if (ts9.isIdentifier(value)) return entityFromIdentifier(value, depth, seen);
|
|
3619
|
+
return propertyEntity(value);
|
|
3620
|
+
}
|
|
3621
|
+
function queryAliasInitializer(identifier, initializers, seen) {
|
|
3622
|
+
const initializer = initializers.get(identifier.text);
|
|
3623
|
+
if (!initializer || seen.has(identifier.text)) return void 0;
|
|
3624
|
+
const binding = resolveBinding2(identifier, identifier);
|
|
3625
|
+
if (!binding.declaration || !binding.immutable || binding.initializer !== initializer || nearestScopedVariableDeclaration(identifier) !== binding.declaration || nonVariableBindingWins(identifier, binding.declaration)) return void 0;
|
|
3626
|
+
seen.add(identifier.text);
|
|
3627
|
+
return initializer;
|
|
3628
|
+
}
|
|
3629
|
+
function queryEntityFromCall(call, initializers, seenInitializers) {
|
|
3630
|
+
const name = expressionName2(call.expression);
|
|
3631
|
+
if (name === "cds.run")
|
|
3632
|
+
return queryEntityFromAst(call.arguments[0], initializers, seenInitializers);
|
|
3633
|
+
if (isCapQueryBuilderRootName(name))
|
|
3634
|
+
return entityFromExpression(call.arguments[0]);
|
|
3635
|
+
const receiver = ts9.isPropertyAccessExpression(call.expression) ? call.expression.expression : void 0;
|
|
3636
|
+
return receiver ? queryEntityFromAst(receiver, initializers, seenInitializers) : void 0;
|
|
3637
|
+
}
|
|
3638
|
+
function queryEntityFromAst(expr, initializers = /* @__PURE__ */ new Map(), seenInitializers = /* @__PURE__ */ new Set()) {
|
|
3639
|
+
const unwrapped = unwrapQueryExpression2(expr);
|
|
3640
|
+
if (ts9.isIdentifier(unwrapped)) {
|
|
3641
|
+
const initializer = queryAliasInitializer(
|
|
3642
|
+
unwrapped,
|
|
3643
|
+
initializers,
|
|
3644
|
+
seenInitializers
|
|
3645
|
+
);
|
|
3646
|
+
return initializer ? queryEntityFromAst(initializer, initializers, seenInitializers) : void 0;
|
|
3647
|
+
}
|
|
3648
|
+
return ts9.isCallExpression(unwrapped) ? queryEntityFromCall(unwrapped, initializers, seenInitializers) : void 0;
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
// src/parsers/outbound-call-parser.ts
|
|
3652
|
+
function lineOf4(text, idx) {
|
|
3653
|
+
return text.slice(0, idx).split("\n").length;
|
|
3654
|
+
}
|
|
3326
3655
|
function queryBuilderEvidence(source, statement) {
|
|
3327
3656
|
return {
|
|
3328
3657
|
classifier: "cap_query_builder_direct",
|
|
@@ -3347,18 +3676,6 @@ function queryRunEvidence(source, argument) {
|
|
|
3347
3676
|
} : {}
|
|
3348
3677
|
};
|
|
3349
3678
|
}
|
|
3350
|
-
function extractQueryEntity(expr) {
|
|
3351
|
-
const source = ts9.createSourceFile("query.ts", `const __query = (${expr});`, ts9.ScriptTarget.Latest, true, ts9.ScriptKind.TS);
|
|
3352
|
-
const initializers = variableInitializers(source);
|
|
3353
|
-
let found;
|
|
3354
|
-
const visit = (node) => {
|
|
3355
|
-
if (found) return;
|
|
3356
|
-
if (ts9.isParenthesizedExpression(node)) found = queryEntityFromAst(node.expression, initializers);
|
|
3357
|
-
ts9.forEachChild(node, visit);
|
|
3358
|
-
};
|
|
3359
|
-
visit(source);
|
|
3360
|
-
return found;
|
|
3361
|
-
}
|
|
3362
3679
|
function queryWarning(expr) {
|
|
3363
3680
|
if (/^\s*[`'"]/.test(expr)) return "raw_sql_or_cql_expression";
|
|
3364
3681
|
if (/^\s*\w+\s*$/.test(expr)) return "query_variable_without_static_initializer";
|
|
@@ -3368,7 +3685,7 @@ function parserEvidence(source, node, extra) {
|
|
|
3368
3685
|
return { parser: "typescript_ast", startOffset: node.getStart(source), endOffset: node.getEnd(), ...extra };
|
|
3369
3686
|
}
|
|
3370
3687
|
function isStringLike(expr) {
|
|
3371
|
-
return Boolean(expr && (
|
|
3688
|
+
return Boolean(expr && (ts10.isStringLiteral(expr) || ts10.isNoSubstitutionTemplateLiteral(expr)));
|
|
3372
3689
|
}
|
|
3373
3690
|
function literalText(expr) {
|
|
3374
3691
|
if (isStringLike(expr)) return expr.text;
|
|
@@ -3376,86 +3693,35 @@ function literalText(expr) {
|
|
|
3376
3693
|
}
|
|
3377
3694
|
function objectPropertyText(object, key) {
|
|
3378
3695
|
const prop = object.properties.find(
|
|
3379
|
-
(property) =>
|
|
3696
|
+
(property) => ts10.isPropertyAssignment(property) && nameOfProperty(property.name) === key || ts10.isShorthandPropertyAssignment(property) && property.name.text === key
|
|
3380
3697
|
);
|
|
3381
3698
|
if (!prop) return void 0;
|
|
3382
|
-
return
|
|
3699
|
+
return ts10.isShorthandPropertyAssignment(prop) ? prop.name.text : prop.initializer.getText();
|
|
3383
3700
|
}
|
|
3384
3701
|
function objectPropertyIsShorthand(object, key) {
|
|
3385
|
-
return object.properties.some((property) =>
|
|
3702
|
+
return object.properties.some((property) => ts10.isShorthandPropertyAssignment(property) && property.name.text === key);
|
|
3386
3703
|
}
|
|
3387
3704
|
function nameOfProperty(name) {
|
|
3388
|
-
if (
|
|
3705
|
+
if (ts10.isIdentifier(name) || ts10.isStringLiteral(name) || ts10.isNumericLiteral(name)) return name.text;
|
|
3389
3706
|
return void 0;
|
|
3390
3707
|
}
|
|
3391
|
-
var maxAliasDepth2 = 5;
|
|
3392
3708
|
function safeRaw(expr) {
|
|
3393
|
-
if (
|
|
3709
|
+
if (ts10.isStringLiteral(expr) || ts10.isNoSubstitutionTemplateLiteral(expr) || ts10.isIdentifier(expr) || ts10.isTemplateExpression(expr)) return expr.getText(expr.getSourceFile());
|
|
3394
3710
|
return void 0;
|
|
3395
3711
|
}
|
|
3396
3712
|
function placeholders2(expr) {
|
|
3397
3713
|
return expr.templateSpans.map((span) => span.expression.getText(expr.getSourceFile()));
|
|
3398
3714
|
}
|
|
3399
|
-
function isFunctionLikeScope(node) {
|
|
3400
|
-
return ts9.isFunctionLike(node) || ts9.isSourceFile(node);
|
|
3401
|
-
}
|
|
3402
|
-
function nodeContains(parent, child) {
|
|
3403
|
-
const source = child.getSourceFile();
|
|
3404
|
-
return child.getStart(source) >= parent.getStart(source) && child.getEnd() <= parent.getEnd();
|
|
3405
|
-
}
|
|
3406
|
-
function declarationScope2(node) {
|
|
3407
|
-
if (ts9.isParameter(node)) return node.parent;
|
|
3408
|
-
if (ts9.isCatchClause(node.parent) && node.parent.variableDeclaration === node) return node.parent;
|
|
3409
|
-
const list = node.parent;
|
|
3410
|
-
const blockScoped = (list.flags & (ts9.NodeFlags.Const | ts9.NodeFlags.Let)) !== 0;
|
|
3411
|
-
let current = list.parent;
|
|
3412
|
-
if (!blockScoped) {
|
|
3413
|
-
while (current.parent && !isFunctionLikeScope(current)) current = current.parent;
|
|
3414
|
-
return current;
|
|
3415
|
-
}
|
|
3416
|
-
while (current.parent && !ts9.isBlock(current) && !ts9.isSourceFile(current) && !ts9.isModuleBlock(current) && !ts9.isCaseBlock(current) && !isLoopInitializerScope(node, current) && !isFunctionLikeScope(current)) current = current.parent;
|
|
3417
|
-
return current;
|
|
3418
|
-
}
|
|
3419
|
-
function isLoopInitializerScope(declaration, scope) {
|
|
3420
|
-
const list = declaration.parent;
|
|
3421
|
-
return ts9.isForStatement(scope) && scope.initializer === list || (ts9.isForInStatement(scope) || ts9.isForOfStatement(scope)) && scope.initializer === list;
|
|
3422
|
-
}
|
|
3423
|
-
function catchBindingScope(declaration) {
|
|
3424
|
-
if (ts9.isParameter(declaration)) return void 0;
|
|
3425
|
-
return ts9.isCatchClause(declaration.parent) && declaration.parent.variableDeclaration === declaration ? declaration.parent : void 0;
|
|
3426
|
-
}
|
|
3427
|
-
function isAccessibleDeclaration(declaration, use) {
|
|
3428
|
-
const source = use.getSourceFile();
|
|
3429
|
-
if (declaration.name.getStart(source) >= use.getStart(source)) return false;
|
|
3430
|
-
const catchScope = catchBindingScope(declaration);
|
|
3431
|
-
if (catchScope) return nodeContains(catchScope.block, use);
|
|
3432
|
-
const scope = declarationScope2(declaration);
|
|
3433
|
-
if (ts9.isForStatement(scope) || ts9.isForInStatement(scope) || ts9.isForOfStatement(scope)) return nodeContains(scope.statement, use);
|
|
3434
|
-
return ts9.isSourceFile(scope) || nodeContains(scope, use);
|
|
3435
|
-
}
|
|
3436
|
-
function resolveBinding2(identifier, use) {
|
|
3437
|
-
const source = use.getSourceFile();
|
|
3438
|
-
let best;
|
|
3439
|
-
const visit = (node) => {
|
|
3440
|
-
if (ts9.isVariableDeclaration(node) && ts9.isIdentifier(node.name) && node.name.text === identifier.text && isAccessibleDeclaration(node, use)) best = node;
|
|
3441
|
-
if (ts9.isParameter(node) && ts9.isIdentifier(node.name) && node.name.text === identifier.text && isAccessibleDeclaration(node, use)) best = node;
|
|
3442
|
-
ts9.forEachChild(node, visit);
|
|
3443
|
-
};
|
|
3444
|
-
visit(source);
|
|
3445
|
-
if (!best) return { immutable: false, evidence: ["binding_not_found"] };
|
|
3446
|
-
const immutable = ts9.isVariableDeclaration(best) && (best.parent.flags & ts9.NodeFlags.Const) !== 0;
|
|
3447
|
-
return { declaration: best, initializer: ts9.isVariableDeclaration(best) ? best.initializer : void 0, immutable, evidence: [immutable ? "lexical_const_binding_before_use" : "lexical_mutable_or_parameter_binding"] };
|
|
3448
|
-
}
|
|
3449
3715
|
function resolveExpression(expr, use, policy, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
3450
3716
|
if (!expr) return { status: "unknown", sourceKind: "dynamic_expression", placeholderKeys: [], evidence: ["expression_missing"] };
|
|
3451
|
-
if (
|
|
3452
|
-
if (
|
|
3453
|
-
if (
|
|
3717
|
+
if (ts10.isStringLiteral(expr)) return { status: "static", sourceKind: "string_literal", value: expr.text, rawExpression: safeRaw(expr), placeholderKeys: [], evidence: ["string_literal"] };
|
|
3718
|
+
if (ts10.isNoSubstitutionTemplateLiteral(expr)) return { status: "static", sourceKind: "no_substitution_template", value: expr.text, rawExpression: safeRaw(expr), placeholderKeys: [], evidence: ["no_substitution_template"] };
|
|
3719
|
+
if (ts10.isTemplateExpression(expr)) {
|
|
3454
3720
|
const keys = placeholders2(expr);
|
|
3455
3721
|
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"] };
|
|
3456
3722
|
return { status: "dynamic", sourceKind: "template_with_substitutions", placeholderKeys: keys, evidence: ["template_substitutions_not_static_external_target"] };
|
|
3457
3723
|
}
|
|
3458
|
-
if (
|
|
3724
|
+
if (ts10.isIdentifier(expr)) {
|
|
3459
3725
|
if (depth >= maxAliasDepth2) return { status: "unknown", sourceKind: "const_alias", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: ["alias_depth_exceeded"], constName: expr.text };
|
|
3460
3726
|
const binding = resolveBinding2(expr, use);
|
|
3461
3727
|
if (!binding.declaration || !binding.initializer || !binding.immutable) return { status: "dynamic", sourceKind: "dynamic_expression", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: binding.evidence, constName: expr.text };
|
|
@@ -3464,12 +3730,12 @@ function resolveExpression(expr, use, policy, depth = 0, seen = /* @__PURE__ */
|
|
|
3464
3730
|
const resolved3 = resolveExpression(binding.initializer, binding.declaration, policy, depth + 1, seen);
|
|
3465
3731
|
return { ...resolved3, sourceKind: "const_alias", rawExpression: safeRaw(expr), constName: expr.text, evidence: [...binding.evidence, ...resolved3.evidence] };
|
|
3466
3732
|
}
|
|
3467
|
-
return { status: "dynamic", sourceKind: "dynamic_expression", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: [`unsupported_${
|
|
3733
|
+
return { status: "dynamic", sourceKind: "dynamic_expression", rawExpression: safeRaw(expr), placeholderKeys: [], evidence: [`unsupported_${ts10.SyntaxKind[expr.kind] ?? "expression"}`] };
|
|
3468
3734
|
}
|
|
3469
3735
|
function staticExpressionText(expr, initializers) {
|
|
3470
3736
|
if (!expr) return void 0;
|
|
3471
3737
|
if (isStringLike(expr)) return expr.text;
|
|
3472
|
-
if (
|
|
3738
|
+
if (ts10.isIdentifier(expr) && initializers.has(expr.text)) return staticExpressionText(initializers.get(expr.text), initializers);
|
|
3473
3739
|
return void 0;
|
|
3474
3740
|
}
|
|
3475
3741
|
function operationPathFromStatic(text) {
|
|
@@ -3477,17 +3743,17 @@ function operationPathFromStatic(text) {
|
|
|
3477
3743
|
}
|
|
3478
3744
|
function destinationExpressionShape(expr) {
|
|
3479
3745
|
if (!expr) return void 0;
|
|
3480
|
-
if (
|
|
3481
|
-
if (
|
|
3482
|
-
if (
|
|
3483
|
-
if (
|
|
3484
|
-
if (
|
|
3485
|
-
if (
|
|
3486
|
-
return
|
|
3746
|
+
if (ts10.isIdentifier(expr)) return "identifier";
|
|
3747
|
+
if (ts10.isPropertyAccessExpression(expr) || ts10.isElementAccessExpression(expr)) return "property_read";
|
|
3748
|
+
if (ts10.isCallExpression(expr)) return "function_call";
|
|
3749
|
+
if (ts10.isConditionalExpression(expr)) return "conditional";
|
|
3750
|
+
if (ts10.isBinaryExpression(expr)) return "binary_expression";
|
|
3751
|
+
if (ts10.isTemplateExpression(expr)) return "template_expression";
|
|
3752
|
+
return ts10.SyntaxKind[expr.kind] ?? "expression";
|
|
3487
3753
|
}
|
|
3488
3754
|
function staticConditionalCandidates(expr, initializers) {
|
|
3489
|
-
const resolved3 = expr &&
|
|
3490
|
-
if (!resolved3 || !
|
|
3755
|
+
const resolved3 = expr && ts10.isIdentifier(expr) && initializers.has(expr.text) ? initializers.get(expr.text) : expr;
|
|
3756
|
+
if (!resolved3 || !ts10.isConditionalExpression(resolved3)) return void 0;
|
|
3491
3757
|
const left = staticExpressionText(resolved3.whenTrue, initializers);
|
|
3492
3758
|
const right = staticExpressionText(resolved3.whenFalse, initializers);
|
|
3493
3759
|
if (!left || !right) return void 0;
|
|
@@ -3495,8 +3761,8 @@ function staticConditionalCandidates(expr, initializers) {
|
|
|
3495
3761
|
}
|
|
3496
3762
|
function propertyInitializer(object, key) {
|
|
3497
3763
|
for (const property of object.properties) {
|
|
3498
|
-
if (
|
|
3499
|
-
if (
|
|
3764
|
+
if (ts10.isPropertyAssignment(property) && nameOfProperty(property.name) === key) return property.initializer;
|
|
3765
|
+
if (ts10.isShorthandPropertyAssignment(property) && property.name.text === key) return property.name;
|
|
3500
3766
|
}
|
|
3501
3767
|
return void 0;
|
|
3502
3768
|
}
|
|
@@ -3557,7 +3823,7 @@ function externalHttpEvidence(node, source) {
|
|
|
3557
3823
|
const exprText = expr.getText(source);
|
|
3558
3824
|
if (exprText === "useOrFetchDestination") {
|
|
3559
3825
|
const objectArg = node.arguments[0];
|
|
3560
|
-
if (objectArg &&
|
|
3826
|
+
if (objectArg && ts10.isObjectLiteralExpression(objectArg)) {
|
|
3561
3827
|
const destination = destinationTargetFromExpression(propertyInitializer(objectArg, "destinationName"), node);
|
|
3562
3828
|
return { externalTarget: destination ?? { kind: "unknown", dynamic: false }, classifier: "sap_destination_lookup", sourceCallShape: "useOrFetchDestination" };
|
|
3563
3829
|
}
|
|
@@ -3565,13 +3831,13 @@ function externalHttpEvidence(node, source) {
|
|
|
3565
3831
|
if (exprText === "executeHttpRequest") {
|
|
3566
3832
|
const destination = destinationTargetFromExpression(node.arguments[0], node);
|
|
3567
3833
|
const config = node.arguments[1];
|
|
3568
|
-
const method = config &&
|
|
3569
|
-
const url = config &&
|
|
3834
|
+
const method = config && ts10.isObjectLiteralExpression(config) ? httpMethodFromObject(config, node) : void 0;
|
|
3835
|
+
const url = config && ts10.isObjectLiteralExpression(config) ? urlTargetFromExpression(propertyInitializer(config, "url"), node) : { kind: "unknown", dynamic: false };
|
|
3570
3836
|
return { method, externalTarget: destination ? { ...url, destination } : url, classifier: "sap_execute_http_request", sourceCallShape: "executeHttpRequest" };
|
|
3571
3837
|
}
|
|
3572
3838
|
if (exprText === "axios") {
|
|
3573
3839
|
const config = node.arguments[0];
|
|
3574
|
-
if (config &&
|
|
3840
|
+
if (config && ts10.isObjectLiteralExpression(config)) {
|
|
3575
3841
|
const method = httpMethodFromObject(config, node);
|
|
3576
3842
|
return { method, externalTarget: urlTargetFromExpression(propertyInitializer(config, "url"), node), classifier: "axios_config_call", sourceCallShape: "axios(config)" };
|
|
3577
3843
|
}
|
|
@@ -3579,10 +3845,10 @@ function externalHttpEvidence(node, source) {
|
|
|
3579
3845
|
}
|
|
3580
3846
|
if (exprText === "fetch") {
|
|
3581
3847
|
const init = node.arguments[1];
|
|
3582
|
-
const method = init &&
|
|
3848
|
+
const method = init && ts10.isObjectLiteralExpression(init) ? httpMethodFromObject(init, node) : void 0;
|
|
3583
3849
|
return { method, externalTarget: urlTargetFromExpression(node.arguments[0], node), classifier: "fetch_call", sourceCallShape: "fetch" };
|
|
3584
3850
|
}
|
|
3585
|
-
if (
|
|
3851
|
+
if (ts10.isPropertyAccessExpression(expr) && ["get", "post", "put", "patch", "delete", "head"].includes(expr.name.text) && expr.expression.getText(source) === "axios") {
|
|
3586
3852
|
return { method: expr.name.text.toUpperCase(), externalTarget: urlTargetFromExpression(node.arguments[0], node), classifier: "axios_member_call", sourceCallShape: `axios.${expr.name.text}` };
|
|
3587
3853
|
}
|
|
3588
3854
|
return void 0;
|
|
@@ -3590,27 +3856,27 @@ function externalHttpEvidence(node, source) {
|
|
|
3590
3856
|
function collectServiceVariables(source) {
|
|
3591
3857
|
const vars = /* @__PURE__ */ new Set(["cds", "messaging", "messageClient", "eventClient"]);
|
|
3592
3858
|
const visit = (node) => {
|
|
3593
|
-
if (
|
|
3859
|
+
if (ts10.isVariableDeclaration(node) && ts10.isIdentifier(node.name) && node.initializer) {
|
|
3594
3860
|
const text = node.initializer.getText(source);
|
|
3595
3861
|
if (/cds\.connect\.(to|messaging)\s*\(/.test(text)) vars.add(node.name.text);
|
|
3596
3862
|
}
|
|
3597
|
-
|
|
3863
|
+
ts10.forEachChild(node, visit);
|
|
3598
3864
|
};
|
|
3599
3865
|
visit(source);
|
|
3600
3866
|
return vars;
|
|
3601
3867
|
}
|
|
3602
3868
|
function receiverName(expr) {
|
|
3603
|
-
if (
|
|
3604
|
-
if (
|
|
3869
|
+
if (ts10.isIdentifier(expr)) return expr.text;
|
|
3870
|
+
if (ts10.isPropertyAccessExpression(expr)) return expr.getText(sourceOf(expr));
|
|
3605
3871
|
return void 0;
|
|
3606
3872
|
}
|
|
3607
3873
|
function sourceOf(node) {
|
|
3608
3874
|
return node.getSourceFile();
|
|
3609
3875
|
}
|
|
3610
3876
|
function rootReceiverName(expr) {
|
|
3611
|
-
if (
|
|
3612
|
-
if (
|
|
3613
|
-
if (
|
|
3877
|
+
if (ts10.isIdentifier(expr)) return expr.text;
|
|
3878
|
+
if (ts10.isPropertyAccessExpression(expr)) return rootReceiverName(expr.expression);
|
|
3879
|
+
if (ts10.isCallExpression(expr)) return rootReceiverName(expr.expression);
|
|
3614
3880
|
return void 0;
|
|
3615
3881
|
}
|
|
3616
3882
|
function isSupportedEventReceiver(receiver, rootReceiver, serviceVariables) {
|
|
@@ -3627,38 +3893,38 @@ function collectWrapperSpecs(source) {
|
|
|
3627
3893
|
const serviceVariables = collectServiceVariables(source);
|
|
3628
3894
|
const calledNames = /* @__PURE__ */ new Set();
|
|
3629
3895
|
const collectCalls = (node) => {
|
|
3630
|
-
if (
|
|
3896
|
+
if (ts10.isCallExpression(node) && ts10.isIdentifier(node.expression))
|
|
3631
3897
|
calledNames.add(node.expression.text);
|
|
3632
|
-
if (
|
|
3898
|
+
if (ts10.isCallExpression(node) && ts10.isCallExpression(node.expression) && ts10.isIdentifier(node.expression.expression))
|
|
3633
3899
|
calledNames.add(node.expression.expression.text);
|
|
3634
|
-
|
|
3900
|
+
ts10.forEachChild(node, collectCalls);
|
|
3635
3901
|
};
|
|
3636
3902
|
collectCalls(source);
|
|
3637
3903
|
const scanFunction = (name, fn) => {
|
|
3638
3904
|
if (!calledNames.has(name) && !isExportedWrapper(fn)) return;
|
|
3639
|
-
const params = fn.parameters.map((param) =>
|
|
3905
|
+
const params = fn.parameters.map((param) => ts10.isIdentifier(param.name) ? param.name.text : void 0);
|
|
3640
3906
|
const sends = [];
|
|
3641
3907
|
const visit = (node) => {
|
|
3642
|
-
if (
|
|
3908
|
+
if (ts10.isCallExpression(node) && ts10.isPropertyAccessExpression(node.expression) && node.expression.name.text === "send" && ts10.isIdentifier(node.expression.expression)) {
|
|
3643
3909
|
const objectArg = node.arguments[0];
|
|
3644
|
-
if (objectArg &&
|
|
3910
|
+
if (objectArg && ts10.isObjectLiteralExpression(objectArg)) {
|
|
3645
3911
|
const pathProp = propertyInitializer(objectArg, "path");
|
|
3646
3912
|
const methodProp = propertyInitializer(objectArg, "method");
|
|
3647
|
-
const pathName = pathProp &&
|
|
3648
|
-
const methodName2 = methodProp &&
|
|
3913
|
+
const pathName = pathProp && ts10.isIdentifier(pathProp) ? pathProp.text : void 0;
|
|
3914
|
+
const methodName2 = methodProp && ts10.isIdentifier(methodProp) ? methodProp.text : void 0;
|
|
3649
3915
|
const methodLiteral = resolveExpression(methodProp, node, "literal").value;
|
|
3650
3916
|
if (pathName) sends.push({ client: node.expression.expression.text, path: pathName, method: methodName2, methodLiteral, start: node.getStart(source), end: node.getEnd() });
|
|
3651
3917
|
}
|
|
3652
3918
|
}
|
|
3653
|
-
if (
|
|
3919
|
+
if (ts10.isCallExpression(node) && ts10.isIdentifier(node.expression) && specs.has(node.expression.text)) {
|
|
3654
3920
|
const nested = specs.get(node.expression.text);
|
|
3655
3921
|
const pathArg = nested ? node.arguments[nested.pathIndex] : void 0;
|
|
3656
3922
|
const clientArg = nested?.clientIndex === void 0 ? void 0 : node.arguments[nested.clientIndex];
|
|
3657
|
-
const pathName = pathArg &&
|
|
3658
|
-
const clientName = clientArg &&
|
|
3923
|
+
const pathName = pathArg && ts10.isIdentifier(pathArg) ? pathArg.text : void 0;
|
|
3924
|
+
const clientName = clientArg && ts10.isIdentifier(clientArg) ? clientArg.text : nested?.clientName;
|
|
3659
3925
|
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() });
|
|
3660
3926
|
}
|
|
3661
|
-
|
|
3927
|
+
ts10.forEachChild(node, visit);
|
|
3662
3928
|
};
|
|
3663
3929
|
visit(fn);
|
|
3664
3930
|
if (sends.length !== 1) return;
|
|
@@ -3670,17 +3936,17 @@ function collectWrapperSpecs(source) {
|
|
|
3670
3936
|
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 });
|
|
3671
3937
|
};
|
|
3672
3938
|
const visitTop = (node) => {
|
|
3673
|
-
if (
|
|
3674
|
-
if (
|
|
3675
|
-
|
|
3939
|
+
if (ts10.isFunctionDeclaration(node) && node.name) scanFunction(node.name.text, node);
|
|
3940
|
+
if (ts10.isVariableDeclaration(node) && ts10.isIdentifier(node.name) && node.initializer && (ts10.isArrowFunction(node.initializer) || ts10.isFunctionExpression(node.initializer))) scanFunction(node.name.text, node.initializer);
|
|
3941
|
+
ts10.forEachChild(node, visitTop);
|
|
3676
3942
|
};
|
|
3677
3943
|
visitTop(source);
|
|
3678
3944
|
return specs;
|
|
3679
3945
|
}
|
|
3680
3946
|
function isExportedWrapper(fn) {
|
|
3681
|
-
const declaration =
|
|
3682
|
-
if (!declaration || !
|
|
3683
|
-
return
|
|
3947
|
+
const declaration = ts10.isFunctionDeclaration(fn) ? fn : ts10.isVariableDeclaration(fn.parent) ? fn.parent.parent.parent : void 0;
|
|
3948
|
+
if (!declaration || !ts10.canHaveModifiers(declaration)) return false;
|
|
3949
|
+
return ts10.getModifiers(declaration)?.some((modifier) => modifier.kind === ts10.SyntaxKind.ExportKeyword) ?? false;
|
|
3684
3950
|
}
|
|
3685
3951
|
function classifyOutboundCallsInSource(source, filePath) {
|
|
3686
3952
|
const calls = [];
|
|
@@ -3693,7 +3959,7 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3693
3959
|
calls.push({ node, fact: { ...fact, sourceFile, sourceLine: lineOf4(source.text, node.getStart(source)), confidence: fact.confidence ?? 0.8, evidence: parserEvidence(source, node, extra) } });
|
|
3694
3960
|
};
|
|
3695
3961
|
const visit = (node) => {
|
|
3696
|
-
if (
|
|
3962
|
+
if (ts10.isCallExpression(node)) {
|
|
3697
3963
|
if (wrapperInternalRanges.some((range) => node.getStart(source) >= range.start && node.getEnd() <= range.end)) {
|
|
3698
3964
|
return;
|
|
3699
3965
|
}
|
|
@@ -3709,11 +3975,11 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3709
3975
|
const entity = queryEntityFromAst(directQuery.logicalCall, initializers);
|
|
3710
3976
|
const payload = directQuery.logicalCall.getText(source);
|
|
3711
3977
|
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));
|
|
3712
|
-
} else if (
|
|
3978
|
+
} else if (ts10.isPropertyAccessExpression(expr) && expr.name.text === "send" && (ts10.isIdentifier(expr.expression) || ts10.isPropertyAccessExpression(expr.expression))) {
|
|
3713
3979
|
const objectArg = node.arguments[0];
|
|
3714
|
-
if (objectArg &&
|
|
3980
|
+
if (objectArg && ts10.isObjectLiteralExpression(objectArg)) {
|
|
3715
3981
|
const receiver = receiverName(expr.expression);
|
|
3716
|
-
const
|
|
3982
|
+
const queryExpression = propertyInitializer(objectArg, "query");
|
|
3717
3983
|
const method = stripQuotes(resolveExpression(propertyInitializer(objectArg, "method"), node, "literal").value ?? objectPropertyText(objectArg, "method") ?? "POST");
|
|
3718
3984
|
const pathExpr = propertyInitializer(objectArg, "path") ?? propertyInitializer(objectArg, "event");
|
|
3719
3985
|
const pathAnalysis = analyzeOperationPath(pathExpr, node, method);
|
|
@@ -3724,8 +3990,9 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3724
3990
|
const entityCallTypes = { entity_mutation: "remote_entity_mutation", entity_delete: "remote_entity_delete", entity_media: "remote_entity_media", entity_candidate: "remote_entity_candidate" };
|
|
3725
3991
|
const entityCallType = entityCallTypes[intent.kind];
|
|
3726
3992
|
const isODataQueryRead = method.toUpperCase() === "GET" && ["entity_query", "entity_key_read", "entity_navigation_query"].includes(intent.kind);
|
|
3727
|
-
const
|
|
3728
|
-
|
|
3993
|
+
const queryEntity = queryExpression ? queryEntityFromAst(queryExpression, initializers) : isODataQueryRead ? intent.entitySegment : void 0;
|
|
3994
|
+
const unresolvedReason = queryExpression ? queryEntity ? void 0 : queryWarning(queryExpression.getText(source)) : pathExpr ? pathUnresolvedReason(pathAnalysis) : void 0;
|
|
3995
|
+
add(node, { callType: queryExpression ? "remote_query" : entityCallType ?? (isODataQueryRead ? "remote_query" : "remote_action"), serviceVariableName: receiver, method, operationPathExpr, queryEntity, payloadSummary: summarizeExpression(objectArg.getText(source)), confidence: op || queryExpression ? 0.8 : 0.4, unresolvedReason }, { receiver, classifier: "service_client_send_object", operationPathExpression: shorthandPath ? op : void 0, rawPathExpression: pathAnalysis.rawExpression, literalPathSource: literalPathSource(pathAnalysis), odataPathIntent: operationPathExpr ? intent : void 0, pathAnalysis, staticPathCandidates: legacyPathCandidates(pathAnalysis), parserWarning: unresolvedReason });
|
|
3729
3996
|
} else {
|
|
3730
3997
|
const receiver = receiverName(expr.expression);
|
|
3731
3998
|
const rootReceiver = rootReceiverName(expr.expression);
|
|
@@ -3744,14 +4011,14 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3744
4011
|
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" });
|
|
3745
4012
|
}
|
|
3746
4013
|
}
|
|
3747
|
-
} else if (
|
|
3748
|
-
const wrapperName =
|
|
3749
|
-
const wrapperArgs =
|
|
4014
|
+
} else if (ts10.isCallExpression(expr) && ts10.isIdentifier(expr.expression) && wrapperSpecs.has(expr.expression.text) || ts10.isIdentifier(expr) && wrapperSpecs.has(expr.text)) {
|
|
4015
|
+
const wrapperName = ts10.isIdentifier(expr) ? expr.text : ts10.isCallExpression(expr) && ts10.isIdentifier(expr.expression) ? expr.expression.text : "";
|
|
4016
|
+
const wrapperArgs = ts10.isIdentifier(expr) ? node.arguments : ts10.isCallExpression(expr) ? expr.arguments : node.arguments;
|
|
3750
4017
|
const spec = wrapperSpecs.get(wrapperName);
|
|
3751
4018
|
const clientArg = spec?.clientIndex === void 0 ? void 0 : wrapperArgs[spec.clientIndex];
|
|
3752
4019
|
const pathArg = spec ? wrapperArgs[spec.pathIndex] : void 0;
|
|
3753
4020
|
const methodArg = spec?.methodIndex === void 0 ? void 0 : wrapperArgs[spec.methodIndex];
|
|
3754
|
-
const receiver = clientArg &&
|
|
4021
|
+
const receiver = clientArg && ts10.isIdentifier(clientArg) ? clientArg.text : spec?.clientName;
|
|
3755
4022
|
const method = stripQuotes(resolveExpression(methodArg, node, "literal").value ?? spec?.methodLiteral ?? "POST");
|
|
3756
4023
|
const pathAnalysis = analyzeOperationPath(pathArg, node, method);
|
|
3757
4024
|
const operationPathExpr = operationPathExpression(pathAnalysis);
|
|
@@ -3762,7 +4029,7 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3762
4029
|
} else if (spec && receiver) {
|
|
3763
4030
|
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 });
|
|
3764
4031
|
}
|
|
3765
|
-
} else if (
|
|
4032
|
+
} else if (ts10.isPropertyAccessExpression(expr) && ["emit", "publish", "on"].includes(expr.name.text)) {
|
|
3766
4033
|
const receiver = receiverName(expr.expression);
|
|
3767
4034
|
const rootReceiver = rootReceiverName(expr.expression);
|
|
3768
4035
|
if (isSupportedEventReceiver(receiver, rootReceiver, serviceVariables)) {
|
|
@@ -3778,7 +4045,7 @@ function classifyOutboundCallsInSource(source, filePath) {
|
|
|
3778
4045
|
}
|
|
3779
4046
|
}
|
|
3780
4047
|
}
|
|
3781
|
-
|
|
4048
|
+
ts10.forEachChild(node, visit);
|
|
3782
4049
|
};
|
|
3783
4050
|
visit(source);
|
|
3784
4051
|
return calls;
|
|
@@ -3792,12 +4059,12 @@ function containsSupportedOutboundCall(node) {
|
|
|
3792
4059
|
async function parseOutboundCalls(repoPath, filePath, context) {
|
|
3793
4060
|
const snapshot = context?.get(filePath);
|
|
3794
4061
|
const text = snapshot?.text ?? await fs8.readFile(path11.join(repoPath, filePath), "utf8");
|
|
3795
|
-
const source = snapshot?.sourceFile() ??
|
|
4062
|
+
const source = snapshot?.sourceFile() ?? ts10.createSourceFile(
|
|
3796
4063
|
filePath,
|
|
3797
4064
|
text,
|
|
3798
|
-
|
|
4065
|
+
ts10.ScriptTarget.Latest,
|
|
3799
4066
|
true,
|
|
3800
|
-
filePath.endsWith(".ts") ?
|
|
4067
|
+
filePath.endsWith(".ts") ? ts10.ScriptKind.TS : ts10.ScriptKind.JS
|
|
3801
4068
|
);
|
|
3802
4069
|
const bindingNames = new Set((await parseServiceBindings(
|
|
3803
4070
|
repoPath,
|
|
@@ -3813,21 +4080,21 @@ async function parseOutboundCalls(repoPath, filePath, context) {
|
|
|
3813
4080
|
);
|
|
3814
4081
|
return [...classifyOutboundCallsInSource(source, filePath).map((call) => call.fact), ...importedWrappers, ...parseLocalServiceCalls(text, filePath, source)];
|
|
3815
4082
|
}
|
|
3816
|
-
function parseLocalServiceCalls(text, filePath, source =
|
|
4083
|
+
function parseLocalServiceCalls(text, filePath, source = ts10.createSourceFile(
|
|
3817
4084
|
filePath,
|
|
3818
4085
|
text,
|
|
3819
|
-
|
|
4086
|
+
ts10.ScriptTarget.Latest,
|
|
3820
4087
|
true,
|
|
3821
|
-
filePath.endsWith(".ts") ?
|
|
4088
|
+
filePath.endsWith(".ts") ? ts10.ScriptKind.TS : ts10.ScriptKind.JS
|
|
3822
4089
|
)) {
|
|
3823
4090
|
const aliases = /* @__PURE__ */ new Map();
|
|
3824
4091
|
const calls = [];
|
|
3825
4092
|
const visit = (node) => {
|
|
3826
|
-
if (
|
|
4093
|
+
if (ts10.isVariableDeclaration(node) && ts10.isIdentifier(node.name) && node.initializer) {
|
|
3827
4094
|
const origin = serviceLookup(node.initializer, aliases);
|
|
3828
4095
|
if (origin) aliases.set(node.name.text, { ...origin, chain: [...origin.chain, node.name.text] });
|
|
3829
4096
|
}
|
|
3830
|
-
if (
|
|
4097
|
+
if (ts10.isCallExpression(node)) {
|
|
3831
4098
|
const parsed = serviceOperationCall(node, aliases);
|
|
3832
4099
|
if (parsed && parsed.operation !== "entities") calls.push({
|
|
3833
4100
|
callType: "local_service_call",
|
|
@@ -3850,20 +4117,20 @@ function parseLocalServiceCalls(text, filePath, source = ts9.createSourceFile(
|
|
|
3850
4117
|
})
|
|
3851
4118
|
});
|
|
3852
4119
|
}
|
|
3853
|
-
|
|
4120
|
+
ts10.forEachChild(node, visit);
|
|
3854
4121
|
};
|
|
3855
4122
|
visit(source);
|
|
3856
4123
|
return calls;
|
|
3857
4124
|
}
|
|
3858
4125
|
function serviceLookup(expr, aliases) {
|
|
3859
|
-
if (
|
|
3860
|
-
if (
|
|
3861
|
-
if (
|
|
4126
|
+
if (ts10.isIdentifier(expr)) return aliases.get(expr.text);
|
|
4127
|
+
if (ts10.isPropertyAccessExpression(expr) && expr.expression.getText() === "cds.services") return { service: expr.name.text, lookup: expr.getText(), chain: [expr.getText()] };
|
|
4128
|
+
if (ts10.isElementAccessExpression(expr) && expr.expression.getText() === "cds.services" && ts10.isStringLiteral(expr.argumentExpression)) return { service: expr.argumentExpression.text, lookup: expr.getText(), chain: [expr.getText()] };
|
|
3862
4129
|
return void 0;
|
|
3863
4130
|
}
|
|
3864
4131
|
function serviceOperationCall(node, aliases) {
|
|
3865
4132
|
const expr = node.expression;
|
|
3866
|
-
if (!
|
|
4133
|
+
if (!ts10.isPropertyAccessExpression(expr)) return void 0;
|
|
3867
4134
|
const origin = serviceLookup(expr.expression, aliases);
|
|
3868
4135
|
if (!origin) return void 0;
|
|
3869
4136
|
if (expr.name.text === "send") {
|
|
@@ -8692,4 +8959,4 @@ export {
|
|
|
8692
8959
|
selectorRepoAmbiguousDiagnostic,
|
|
8693
8960
|
trace
|
|
8694
8961
|
};
|
|
8695
|
-
//# sourceMappingURL=chunk-
|
|
8962
|
+
//# sourceMappingURL=chunk-BGD7UYJN.js.map
|