@saptools/service-flow 0.1.35 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -998,47 +998,51 @@ function isSupportedEventReceiver(receiver, rootReceiver, serviceVariables) {
998
998
  }
999
999
  function collectWrapperSpecs(source) {
1000
1000
  const specs = /* @__PURE__ */ new Map();
1001
+ const serviceVariables = collectServiceVariables(source);
1002
+ const calledNames = /* @__PURE__ */ new Set();
1003
+ const collectCalls = (node) => {
1004
+ if (ts4.isCallExpression(node)) {
1005
+ if (ts4.isIdentifier(node.expression)) calledNames.add(node.expression.text);
1006
+ if (ts4.isCallExpression(node.expression) && ts4.isIdentifier(node.expression.expression)) calledNames.add(node.expression.expression.text);
1007
+ }
1008
+ ts4.forEachChild(node, collectCalls);
1009
+ };
1010
+ collectCalls(source);
1001
1011
  const scanFunction = (name, fn) => {
1012
+ if (!calledNames.has(name)) return;
1002
1013
  const params = fn.parameters.map((param) => ts4.isIdentifier(param.name) ? param.name.text : void 0);
1003
- const closure = returnedClosure(fn);
1004
- if (!closure) return;
1005
1014
  const sends = [];
1006
1015
  const visit = (node) => {
1007
1016
  if (ts4.isCallExpression(node) && ts4.isPropertyAccessExpression(node.expression) && node.expression.name.text === "send" && ts4.isIdentifier(node.expression.expression)) {
1008
1017
  const objectArg = node.arguments[0];
1009
1018
  if (objectArg && ts4.isObjectLiteralExpression(objectArg)) {
1010
- const pathProp = objectArg.properties.find((property) => ts4.isShorthandPropertyAssignment(property) && property.name.text === "path");
1011
- if (pathProp) sends.push({ client: node.expression.expression.text, path: pathProp.name.text, method: objectPropertyText(objectArg, "method") });
1019
+ const pathProp = propertyInitializer(objectArg, "path");
1020
+ const methodProp = propertyInitializer(objectArg, "method");
1021
+ const pathName = pathProp && ts4.isIdentifier(pathProp) ? pathProp.text : void 0;
1022
+ const methodName = methodProp && ts4.isIdentifier(methodProp) ? methodProp.text : void 0;
1023
+ const methodLiteral = staticExpressionText(methodProp, variableInitializers(source));
1024
+ if (pathName) sends.push({ client: node.expression.expression.text, path: pathName, method: methodName, methodLiteral, start: node.getStart(source), end: node.getEnd() });
1012
1025
  }
1013
1026
  }
1014
1027
  ts4.forEachChild(node, visit);
1015
1028
  };
1016
- visit(closure);
1029
+ visit(fn);
1017
1030
  if (sends.length !== 1) return;
1018
1031
  const found = sends[0];
1019
1032
  const clientIndex = params.indexOf(found.client);
1020
1033
  const pathIndex = params.indexOf(found.path);
1021
- const methodIndex = found.method && params.includes(found.method) ? params.indexOf(found.method) : void 0;
1022
- if (clientIndex >= 0 && pathIndex >= 0) specs.set(name, { clientIndex, pathIndex, methodIndex, definitionLine: lineOf3(source.text, fn.getStart(source)), internalStart: closure.getStart(source), internalEnd: closure.getEnd() });
1034
+ const methodIndex = found.method ? params.indexOf(found.method) : -1;
1035
+ const capturesKnownClient = serviceVariables.has(found.client) || /^(srv|service|serviceClient|client|.*Client)$/.test(found.client);
1036
+ if (pathIndex >= 0 && (clientIndex >= 0 || capturesKnownClient)) specs.set(name, { clientIndex: clientIndex >= 0 ? clientIndex : void 0, clientName: clientIndex >= 0 ? void 0 : found.client, pathIndex, methodIndex: methodIndex >= 0 ? methodIndex : void 0, methodName: found.method, methodLiteral: found.methodLiteral, definitionLine: lineOf3(source.text, fn.getStart(source)), internalStart: found.start, internalEnd: found.end });
1023
1037
  };
1024
- for (const stmt of source.statements) {
1025
- if (ts4.isFunctionDeclaration(stmt) && stmt.name) scanFunction(stmt.name.text, stmt);
1026
- if (ts4.isVariableStatement(stmt)) {
1027
- for (const decl of stmt.declarationList.declarations) if (ts4.isIdentifier(decl.name) && decl.initializer && (ts4.isArrowFunction(decl.initializer) || ts4.isFunctionExpression(decl.initializer))) scanFunction(decl.name.text, decl.initializer);
1028
- }
1029
- }
1038
+ const visitTop = (node) => {
1039
+ if (ts4.isFunctionDeclaration(node) && node.name) scanFunction(node.name.text, node);
1040
+ if (ts4.isVariableDeclaration(node) && ts4.isIdentifier(node.name) && node.initializer && (ts4.isArrowFunction(node.initializer) || ts4.isFunctionExpression(node.initializer))) scanFunction(node.name.text, node.initializer);
1041
+ ts4.forEachChild(node, visitTop);
1042
+ };
1043
+ visitTop(source);
1030
1044
  return specs;
1031
1045
  }
1032
- function returnedClosure(fn) {
1033
- const body = fn.body;
1034
- if (!body) return void 0;
1035
- if (ts4.isArrowFunction(fn) && !ts4.isBlock(body)) return ts4.isArrowFunction(body) || ts4.isFunctionExpression(body) ? body : void 0;
1036
- if (!ts4.isBlock(body)) return void 0;
1037
- const returns = body.statements.filter(ts4.isReturnStatement);
1038
- if (returns.length !== 1) return void 0;
1039
- const expr = returns[0]?.expression;
1040
- return expr && (ts4.isArrowFunction(expr) || ts4.isFunctionExpression(expr)) ? expr : void 0;
1041
- }
1042
1046
  function classifyOutboundCallsInSource(source, filePath) {
1043
1047
  const calls = [];
1044
1048
  const sourceFile = normalizePath(filePath);
@@ -1067,26 +1071,31 @@ function classifyOutboundCallsInSource(source, filePath) {
1067
1071
  const receiver = receiverName(expr.expression);
1068
1072
  const query = objectPropertyText(objectArg, "query");
1069
1073
  const method = stripQuotes(objectPropertyText(objectArg, "method") ?? "POST");
1070
- const op = objectPropertyText(objectArg, "path") ?? objectPropertyText(objectArg, "event");
1074
+ const pathExpr = propertyInitializer(objectArg, "path") ?? propertyInitializer(objectArg, "event");
1075
+ const op = pathExpr ? staticExpressionText(pathExpr, initializers) ?? pathExpr.getText(source) : void 0;
1071
1076
  const shorthandPath = objectPropertyIsShorthand(objectArg, "path");
1072
- const operationPathExpr = op && !shorthandPath ? `/${stripQuotes(op).replace(/^\//, "")}` : void 0;
1077
+ const staticPath = staticExpressionText(pathExpr, initializers);
1078
+ const literalPath = isStringLike(pathExpr) ? pathExpr.text : pathExpr && ts4.isTemplateExpression(pathExpr) ? pathExpr.getText(source) : void 0;
1079
+ const operationPathExpr = staticPath ? `/${stripQuotes(staticPath).replace(/^\//, "")}` : literalPath ? `/${stripQuotes(literalPath).replace(/^\//, "")}` : void 0;
1073
1080
  const intent = classifyODataPathIntent(operationPathExpr, method);
1074
1081
  const entityCallTypes = { entity_mutation: "remote_entity_mutation", entity_delete: "remote_entity_delete", entity_media: "remote_entity_media", entity_candidate: "remote_entity_candidate" };
1075
1082
  const entityCallType = entityCallTypes[intent.kind];
1076
1083
  const isODataQueryRead = method.toUpperCase() === "GET" && ["entity_query", "entity_key_read", "entity_navigation_query"].includes(intent.kind);
1077
- add(node, { callType: query ? "remote_query" : entityCallType ?? (isODataQueryRead ? "remote_query" : "remote_action"), serviceVariableName: receiver, method, operationPathExpr, queryEntity: query ? extractQueryEntity(query) : isODataQueryRead ? intent.entitySegment : void 0, payloadSummary: summarizeExpression(objectArg.getText(source)), confidence: op || query ? 0.8 : 0.4, unresolvedReason: !query && shorthandPath ? "dynamic_operation_path_identifier" : void 0 }, { receiver, classifier: "service_client_send_object", operationPathExpression: shorthandPath ? op : void 0, odataPathIntent: operationPathExpr ? intent : void 0, parserWarning: shorthandPath ? "dynamic_operation_path_identifier" : void 0 });
1084
+ add(node, { callType: query ? "remote_query" : entityCallType ?? (isODataQueryRead ? "remote_query" : "remote_action"), serviceVariableName: receiver, method, operationPathExpr, queryEntity: query ? extractQueryEntity(query) : isODataQueryRead ? intent.entitySegment : void 0, payloadSummary: summarizeExpression(objectArg.getText(source)), confidence: op || query ? 0.8 : 0.4, unresolvedReason: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 }, { receiver, classifier: "service_client_send_object", operationPathExpression: shorthandPath ? op : void 0, literalPathSource: shorthandPath && staticPath ? "same_scope_const_initializer" : void 0, odataPathIntent: operationPathExpr ? intent : void 0, parserWarning: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 });
1078
1085
  }
1079
1086
  } else if (ts4.isCallExpression(expr) && ts4.isIdentifier(expr.expression) && wrapperSpecs.has(expr.expression.text) || ts4.isIdentifier(expr) && wrapperSpecs.has(expr.text)) {
1080
1087
  const wrapperName = ts4.isIdentifier(expr) ? expr.text : ts4.isCallExpression(expr) && ts4.isIdentifier(expr.expression) ? expr.expression.text : "";
1081
1088
  const wrapperArgs = ts4.isIdentifier(expr) ? node.arguments : ts4.isCallExpression(expr) ? expr.arguments : node.arguments;
1082
1089
  const spec = wrapperSpecs.get(wrapperName);
1083
- const clientArg = spec ? wrapperArgs[spec.clientIndex] : void 0;
1090
+ const clientArg = spec?.clientIndex === void 0 ? void 0 : wrapperArgs[spec.clientIndex];
1084
1091
  const pathArg = spec ? wrapperArgs[spec.pathIndex] : void 0;
1085
1092
  const methodArg = spec?.methodIndex === void 0 ? void 0 : wrapperArgs[spec.methodIndex];
1086
- if (spec && clientArg && ts4.isIdentifier(clientArg) && isStringLike(pathArg)) {
1087
- add(node, { callType: "remote_action", serviceVariableName: clientArg.text, method: stripQuotes(methodArg?.getText(source) ?? "POST"), operationPathExpr: `/${pathArg.text.replace(/^\//, "")}`, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.75 }, { receiver: clientArg.text, classifier: "higher_order_wrapper_literal_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, literalCallerArgumentDetected: true });
1088
- } else if (spec && clientArg && ts4.isIdentifier(clientArg)) {
1089
- add(node, { callType: "remote_action", serviceVariableName: clientArg.text, method: stripQuotes(methodArg?.getText(source) ?? "POST"), payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.45, unresolvedReason: "dynamic_operation_path_identifier" }, { receiver: clientArg.text, classifier: "higher_order_wrapper_dynamic_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, parserWarning: "dynamic_operation_path_identifier" });
1093
+ const receiver = clientArg && ts4.isIdentifier(clientArg) ? clientArg.text : spec?.clientName;
1094
+ const method = stripQuotes(staticExpressionText(methodArg, initializers) ?? spec?.methodLiteral ?? "POST");
1095
+ if (spec && receiver && isStringLike(pathArg)) {
1096
+ add(node, { callType: "remote_action", serviceVariableName: receiver, method, operationPathExpr: `/${pathArg.text.replace(/^\//, "")}`, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.75 }, { receiver, classifier: "higher_order_wrapper_literal_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), literalPathSource: "wrapper_call_argument", literalCallerArgumentDetected: true });
1097
+ } else if (spec && receiver) {
1098
+ add(node, { callType: "remote_action", serviceVariableName: receiver, method, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.45, unresolvedReason: "dynamic_operation_path_identifier" }, { receiver, classifier: "higher_order_wrapper_dynamic_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), parserWarning: "dynamic_operation_path_identifier" });
1090
1099
  }
1091
1100
  } else if (ts4.isPropertyAccessExpression(expr) && ["emit", "publish", "on"].includes(expr.name.text)) {
1092
1101
  const receiver = receiverName(expr.expression);
@@ -1257,6 +1266,17 @@ function unwrapIdentityExpression(expr) {
1257
1266
  if (ts5.isTypeAssertionExpression(expr)) return unwrapIdentityExpression(expr.expression);
1258
1267
  return expr;
1259
1268
  }
1269
+ function transactionReceiverName(expr) {
1270
+ const call = unwrapCall(expr);
1271
+ if (call && ts5.isPropertyAccessExpression(call.expression) && ["tx", "transaction"].includes(call.expression.name.text) && ts5.isIdentifier(call.expression.expression)) return call.expression.expression.text;
1272
+ const unwrapped = unwrapIdentityExpression(expr);
1273
+ if (ts5.isConditionalExpression(unwrapped)) {
1274
+ const left = transactionReceiverName(unwrapped.whenTrue);
1275
+ const right = transactionReceiverName(unwrapped.whenFalse);
1276
+ return left && left === right ? left : void 0;
1277
+ }
1278
+ return void 0;
1279
+ }
1260
1280
  function findConnectInExpression(expr) {
1261
1281
  const direct = unwrapCall(expr);
1262
1282
  if (direct) {
@@ -1335,16 +1355,31 @@ async function importsFor(repoPath, filePath, sf) {
1335
1355
  }
1336
1356
  return imports;
1337
1357
  }
1338
- function collectReturnedObjectBindings(fn) {
1358
+ function collectLocalBindingFacts(fn) {
1339
1359
  const bindings = /* @__PURE__ */ new Map();
1340
- const returns = /* @__PURE__ */ new Map();
1341
1360
  function visit(node) {
1342
1361
  if (node !== fn && (ts5.isFunctionDeclaration(node) || ts5.isArrowFunction(node) || ts5.isFunctionExpression(node)))
1343
1362
  return;
1344
1363
  if (ts5.isVariableDeclaration(node) && ts5.isIdentifier(node.name) && node.initializer) {
1345
1364
  const fact = findConnectInExpression(node.initializer);
1346
1365
  if (fact) bindings.set(node.name.text, fact);
1366
+ const sourceName = transactionReceiverName(node.initializer);
1367
+ if (sourceName) {
1368
+ const source = bindings.get(sourceName);
1369
+ if (source) bindings.set(node.name.text, { ...source, helperChain: [...source.helperChain ?? [], { aliasOf: sourceName, callerVariable: node.name.text, aliasKind: "transaction", transactionAliasSource: sourceName }] });
1370
+ }
1347
1371
  }
1372
+ ts5.forEachChild(node, visit);
1373
+ }
1374
+ visit(fn);
1375
+ return bindings;
1376
+ }
1377
+ function collectReturnedObjectBindings(fn) {
1378
+ const bindings = collectLocalBindingFacts(fn);
1379
+ const returns = /* @__PURE__ */ new Map();
1380
+ function visit(node) {
1381
+ if (node !== fn && (ts5.isFunctionDeclaration(node) || ts5.isArrowFunction(node) || ts5.isFunctionExpression(node)))
1382
+ return;
1348
1383
  if (ts5.isReturnStatement(node) && node.expression && ts5.isObjectLiteralExpression(node.expression)) {
1349
1384
  for (const prop of node.expression.properties) {
1350
1385
  if (ts5.isShorthandPropertyAssignment(prop)) returns.set(prop.name.text, prop.name.text);
@@ -1370,15 +1405,11 @@ function functionLikeInitializer(expr) {
1370
1405
  return void 0;
1371
1406
  }
1372
1407
  function directReturnConnectFact(fn) {
1373
- const localBindings = /* @__PURE__ */ new Map();
1408
+ const localBindings = collectLocalBindingFacts(fn);
1374
1409
  let returned;
1375
1410
  function visit(node) {
1376
1411
  if (node !== fn && (ts5.isFunctionDeclaration(node) || ts5.isArrowFunction(node) || ts5.isFunctionExpression(node)))
1377
1412
  return;
1378
- if (ts5.isVariableDeclaration(node) && ts5.isIdentifier(node.name) && node.initializer) {
1379
- const fact = findConnectInExpression(node.initializer);
1380
- if (fact) localBindings.set(node.name.text, fact);
1381
- }
1382
1413
  if (!returned && ts5.isReturnStatement(node) && node.expression)
1383
1414
  returned = node.expression;
1384
1415
  if (!returned) ts5.forEachChild(node, visit);
@@ -1482,8 +1513,11 @@ async function parseServiceBindings(repoPath, filePath) {
1482
1513
  const helperCache = /* @__PURE__ */ new Map();
1483
1514
  const classHelpers = collectClassHelpers(sourceFileAst);
1484
1515
  const localObjectHelpers = /* @__PURE__ */ new Map();
1516
+ const localDirectHelpers = /* @__PURE__ */ new Map();
1485
1517
  for (const stmt of sourceFileAst.statements) {
1486
1518
  if (ts5.isFunctionDeclaration(stmt) && stmt.name) {
1519
+ const directFact = directConnectFactFromFunctionLike(stmt);
1520
+ if (directFact) localDirectHelpers.set(stmt.name.text, { ...directFact, exportedName: stmt.name.text, sourceFile: normalizePath(filePath), sourceLine: lineOf4(sourceFileAst, stmt) });
1487
1521
  const rows2 = [];
1488
1522
  for (const [returnedProperty, fact] of collectReturnedObjectBindings(stmt))
1489
1523
  rows2.push({ ...fact, exportedName: stmt.name.text, returnedProperty, sourceFile: normalizePath(filePath), sourceLine: lineOf4(sourceFileAst, stmt) });
@@ -1494,6 +1528,8 @@ async function parseServiceBindings(repoPath, filePath) {
1494
1528
  if (!ts5.isIdentifier(decl.name)) continue;
1495
1529
  const helper = functionLikeInitializer(decl.initializer);
1496
1530
  if (!helper) continue;
1531
+ const directFact = directConnectFactFromFunctionLike(helper);
1532
+ if (directFact) localDirectHelpers.set(decl.name.text, { ...directFact, exportedName: decl.name.text, sourceFile: normalizePath(filePath), sourceLine: lineOf4(sourceFileAst, decl) });
1497
1533
  const rows2 = [];
1498
1534
  for (const [returnedProperty, fact] of collectReturnedObjectBindings(helper))
1499
1535
  rows2.push({ ...fact, exportedName: decl.name.text, returnedProperty, sourceFile: normalizePath(filePath), sourceLine: lineOf4(sourceFileAst, decl) });
@@ -1531,7 +1567,8 @@ async function parseServiceBindings(repoPath, filePath) {
1531
1567
  callerVariable: targetName,
1532
1568
  aliasOf: sourceName,
1533
1569
  aliasKind,
1534
- scopeRule: "same-file-source-order"
1570
+ scopeRule: "same-file-source-order",
1571
+ ...aliasKind === "transaction" ? { transactionAliasSource: sourceName } : {}
1535
1572
  }
1536
1573
  ]
1537
1574
  });
@@ -1555,7 +1592,8 @@ async function parseServiceBindings(repoPath, filePath) {
1555
1592
  helperChain: aliasKind === "assignment" ? [{ callerVariable: targetName, assignedFrom: call.expression.getText(sourceFileAst), aliasKind, scopeRule: "same-file-source-order" }] : void 0
1556
1593
  });
1557
1594
  else if (ts5.isIdentifier(call.expression)) {
1558
- const resolved2 = await importedHelper(call.expression.text);
1595
+ const localDirect = localDirectHelpers.get(call.expression.text);
1596
+ const resolved2 = localDirect ? { helper: localDirect, imp: void 0 } : await importedHelper(call.expression.text);
1559
1597
  if (resolved2)
1560
1598
  out.push({
1561
1599
  variableName: targetName,
@@ -1568,12 +1606,13 @@ async function parseServiceBindings(repoPath, filePath) {
1568
1606
  sourceFile: normalizePath(filePath),
1569
1607
  sourceLine: lineOf4(sourceFileAst, node),
1570
1608
  helperChain: [
1609
+ ...resolved2.helper.helperChain ?? [],
1571
1610
  {
1572
1611
  callerVariable: targetName,
1573
1612
  ...aliasKind === "assignment" ? { assignedFrom: call.expression.text, aliasKind, scopeRule: "same-file-source-order" } : {},
1574
1613
  importedHelper: call.expression.text,
1575
- importSource: resolved2.imp.sourceFile,
1576
- exportedSymbol: resolved2.imp.exportedName,
1614
+ importSource: resolved2.imp?.sourceFile,
1615
+ exportedSymbol: resolved2.imp?.exportedName ?? resolved2.helper.exportedName,
1577
1616
  helperSourceFile: resolved2.helper.sourceFile,
1578
1617
  helperSourceLine: resolved2.helper.sourceLine
1579
1618
  }
@@ -1613,7 +1652,7 @@ async function parseServiceBindings(repoPath, filePath) {
1613
1652
  placeholders: resolved2.helper.placeholders,
1614
1653
  sourceFile: normalizePath(filePath),
1615
1654
  sourceLine: lineOf4(sourceFileAst, decl),
1616
- helperChain: [{ callerVariable: el.name.text, helperFunction: call.expression.getText(sourceFileAst), returnedProperty: propertyName, importSource: resolved2.imp?.sourceFile, exportedSymbol: resolved2.imp?.exportedName, helperSourceFile: resolved2.helper.sourceFile, helperSourceLine: resolved2.helper.sourceLine }]
1655
+ helperChain: [...resolved2.helper.helperChain ?? [], { callerVariable: el.name.text, helperFunction: call.expression.getText(sourceFileAst), returnedProperty: propertyName, importSource: resolved2.imp?.sourceFile, exportedSymbol: resolved2.imp?.exportedName, helperSourceFile: resolved2.helper.sourceFile, helperSourceLine: resolved2.helper.sourceLine }]
1617
1656
  });
1618
1657
  }
1619
1658
  }
@@ -1646,7 +1685,7 @@ async function parseServiceBindings(repoPath, filePath) {
1646
1685
  placeholders: resolved2.helper.placeholders,
1647
1686
  sourceFile: normalizePath(filePath),
1648
1687
  sourceLine: lineOf4(sourceFileAst, node),
1649
- helperChain: [{ callerVariable: targetName, assignedFrom: call.expression.getText(sourceFileAst), aliasKind: "assignment", scopeRule: "same-file-source-order", returnedProperty: propertyName, importSource: resolved2.imp?.sourceFile, exportedSymbol: resolved2.imp?.exportedName, helperSourceFile: resolved2.helper.sourceFile, helperSourceLine: resolved2.helper.sourceLine }]
1688
+ helperChain: [...resolved2.helper.helperChain ?? [], { callerVariable: targetName, assignedFrom: call.expression.getText(sourceFileAst), aliasKind: "assignment", scopeRule: "same-file-source-order", returnedProperty: propertyName, importSource: resolved2.imp?.sourceFile, exportedSymbol: resolved2.imp?.exportedName, helperSourceFile: resolved2.helper.sourceFile, helperSourceLine: resolved2.helper.sourceLine }]
1650
1689
  });
1651
1690
  }
1652
1691
  }
@@ -1682,6 +1721,68 @@ async function parseServiceBindings(repoPath, filePath) {
1682
1721
  });
1683
1722
  }
1684
1723
  }
1724
+ function arrayElementsFromExpression(expr) {
1725
+ const unwrapped = unwrapIdentityExpression(expr);
1726
+ if (ts5.isArrayLiteralExpression(unwrapped)) return { elements: unwrapped.elements, promiseAll: false };
1727
+ const call = unwrapCall(expr);
1728
+ if (!call) return void 0;
1729
+ if (!ts5.isPropertyAccessExpression(call.expression) || call.expression.name.text !== "all" || call.expression.expression.getText(sourceFileAst) !== "Promise") return void 0;
1730
+ const first = call.arguments[0];
1731
+ if (!first) return void 0;
1732
+ const container = unwrapIdentityExpression(first);
1733
+ if (!ts5.isArrayLiteralExpression(container)) return void 0;
1734
+ return { elements: container.elements, promiseAll: true };
1735
+ }
1736
+ async function recordArrayElementBinding(targetName, expr, node, arrayIndex, promiseAll) {
1737
+ const before = out.length;
1738
+ await recordBindingFromExpression(targetName, expr, node, "declaration");
1739
+ if (out.length > before) {
1740
+ const row = out[out.length - 1];
1741
+ row.helperChain = [
1742
+ ...row.helperChain ?? [],
1743
+ { callerVariable: targetName, targetVariable: targetName, arrayIndex, promiseAll, arrayContainer: promiseAll ? "Promise.all" : "array_literal" }
1744
+ ];
1745
+ return;
1746
+ }
1747
+ const unwrapped = unwrapIdentityExpression(expr);
1748
+ if (ts5.isIdentifier(unwrapped)) {
1749
+ const existing = bindingForVariable(unwrapped.text);
1750
+ if (!existing) return;
1751
+ out.push({
1752
+ ...existing,
1753
+ variableName: targetName,
1754
+ sourceLine: lineOf4(sourceFileAst, node),
1755
+ helperChain: [
1756
+ ...existing.helperChain ?? [],
1757
+ { callerVariable: targetName, targetVariable: targetName, sourceVariable: unwrapped.text, aliasKind: "array-destructuring", arrayIndex, promiseAll, arrayContainer: promiseAll ? "Promise.all" : "array_literal" }
1758
+ ]
1759
+ });
1760
+ }
1761
+ }
1762
+ async function recordArrayDestructuredVariable(decl) {
1763
+ if (!ts5.isArrayBindingPattern(decl.name) || !decl.initializer) return;
1764
+ const container = arrayElementsFromExpression(decl.initializer);
1765
+ if (!container) return;
1766
+ for (let index = 0; index < decl.name.elements.length; index += 1) {
1767
+ const el = decl.name.elements[index];
1768
+ if (!el || ts5.isOmittedExpression(el) || ts5.isBindingElement(el) && el.dotDotDotToken) continue;
1769
+ if (!ts5.isBindingElement(el) || !ts5.isIdentifier(el.name)) continue;
1770
+ const source = container.elements[index];
1771
+ if (!source || ts5.isOmittedExpression(source)) continue;
1772
+ await recordArrayElementBinding(el.name.text, source, decl, index, container.promiseAll);
1773
+ }
1774
+ }
1775
+ async function recordArrayDestructuredAssignment(pattern, expr, node) {
1776
+ const container = arrayElementsFromExpression(expr);
1777
+ if (!container) return;
1778
+ for (let index = 0; index < pattern.elements.length; index += 1) {
1779
+ const el = pattern.elements[index];
1780
+ if (!el || ts5.isOmittedExpression(el) || ts5.isSpreadElement(el) || !ts5.isIdentifier(el)) continue;
1781
+ const source = container.elements[index];
1782
+ if (!source || ts5.isOmittedExpression(source)) continue;
1783
+ await recordArrayElementBinding(el.text, source, node, index, container.promiseAll);
1784
+ }
1785
+ }
1685
1786
  const events = [];
1686
1787
  function collectEvents(node) {
1687
1788
  if (ts5.isVariableDeclaration(node)) events.push({ pos: node.getStart(sourceFileAst), node });
@@ -1695,11 +1796,13 @@ async function parseServiceBindings(repoPath, filePath) {
1695
1796
  if (ts5.isVariableDeclaration(event.node)) {
1696
1797
  const decl = event.node;
1697
1798
  await recordDestructuredHelper(decl);
1799
+ await recordArrayDestructuredVariable(decl);
1698
1800
  recordDestructuredClassHelper(decl);
1699
1801
  await recordVariable(decl);
1700
1802
  recordIdentityAlias(decl);
1701
- if (ts5.isIdentifier(decl.name) && decl.initializer && ts5.isCallExpression(decl.initializer) && ts5.isPropertyAccessExpression(decl.initializer.expression) && decl.initializer.expression.name.text === "tx" && ts5.isIdentifier(decl.initializer.expression.expression)) {
1702
- cloneAliasBinding(decl.name.text, decl.initializer.expression.expression.text, "transaction", decl);
1803
+ if (ts5.isIdentifier(decl.name) && decl.initializer) {
1804
+ const sourceName = transactionReceiverName(decl.initializer);
1805
+ if (sourceName) cloneAliasBinding(decl.name.text, sourceName, "transaction", decl);
1703
1806
  }
1704
1807
  continue;
1705
1808
  }
@@ -1716,6 +1819,8 @@ async function parseServiceBindings(repoPath, filePath) {
1716
1819
  const left = ts5.isParenthesizedExpression(assignment.left) ? assignment.left.expression : assignment.left;
1717
1820
  if (ts5.isObjectLiteralExpression(left))
1718
1821
  await recordDestructuredAssignment(left, assignment.right, assignment);
1822
+ if (ts5.isArrayLiteralExpression(left))
1823
+ await recordArrayDestructuredAssignment(left, assignment.right, assignment);
1719
1824
  }
1720
1825
  return out;
1721
1826
  }
@@ -2974,4 +3079,4 @@ export {
2974
3079
  linkWorkspace,
2975
3080
  trace
2976
3081
  };
2977
- //# sourceMappingURL=chunk-R47GA5VW.js.map
3082
+ //# sourceMappingURL=chunk-WE3A6TOJ.js.map